diff --git a/.gitignore b/.gitignore index 5de33077e..1b4e08ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,10 @@ src/jtag/minidriver_imp.h src/jtag/jtag_minidriver.h +# coverage files (gcov) +*.gcda +*.gcno + # OpenULINK driver files generated by SDCC src/jtag/drivers/OpenULINK/*.rel src/jtag/drivers/OpenULINK/*.asm diff --git a/HACKING b/HACKING index 9e8cd357f..8988b1617 100644 --- a/HACKING +++ b/HACKING @@ -92,6 +92,22 @@ patch: make @endcode +- Code coverage analysis + + By inspecting the code coverage, you can identify potential gaps in your testing + and use that information to improve your test scenarios. + + Example usage: + @code + mkdir build-gcov; cd build-gcov + ../configure --enable-gcov [...] + make + # ... Now execute your test scenarios to collect OpenOCD code coverage ... + lcov --capture --directory ./src --output-file openocd-coverage.info + genhtml openocd-coverage.info --output-directory coverage_report + # ... Open coverage_report/index.html in a web browser ... + @endcode + Please consider performing these additional checks where appropriate (especially Clang Static Analyzer for big portions of new code) and mention the results (e.g. "Valgrind-clean, no new Clang analyzer diff --git a/Makefile.am b/Makefile.am index 2230e628f..ab0a2373d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,7 @@ endif # common flags used in openocd build AM_CFLAGS = $(GCC_WARNINGS) +AM_LDFLAGS = AM_CPPFLAGS = $(HOST_CPPFLAGS)\ -I$(top_srcdir)/src \ @@ -51,6 +52,12 @@ AM_CPPFLAGS += -I$(top_srcdir)/jimtcl \ else AM_CPPFLAGS += $(JIMTCL_CFLAGS) endif + +if USE_GCOV +AM_CFLAGS += --coverage +AM_LDFLAGS += --coverage +endif + EXTRA_DIST += \ BUGS \ HACKING \ diff --git a/configure.ac b/configure.ac index 9355ffb93..291e854a4 100644 --- a/configure.ac +++ b/configure.ac @@ -171,6 +171,9 @@ m4_define([DUMMY_ADAPTER], m4_define([OPTIONAL_LIBRARIES], [[[capstone], [Use Capstone disassembly framework], []]]) +m4_define([COVERAGE], + [[[gcov], [Collect coverage using gcov], []]]) + AC_ARG_ENABLE([doxygen-html], AS_HELP_STRING([--disable-doxygen-html], [Disable building Doxygen manual as HTML.]), @@ -199,6 +202,19 @@ AC_ARG_ENABLE([werror], AS_HELP_STRING([--disable-werror], [Do not treat warnings as errors]), [gcc_werror=$enableval], [gcc_werror=$gcc_warnings]) +AC_ARG_ENABLE([gcov], + AS_HELP_STRING([--enable-gcov], [Enable runtime coverage collection via gcov]), + [enable_gcov=$enableval], [enable_gcov=no]) + +AS_IF([test "x$enable_gcov" = "xyes"], [ + AC_DEFINE([USE_GCOV], [1], [1 to enable coverage collection using gcov.]) + dnl When collecting coverage, disable optimizations. + dnl This overrides the "-O2" that autoconf uses by default: + CFLAGS+=" -O0" +], [ + AC_DEFINE([USE_GCOV], [0], [0 to leave coverage collection disabled.]) +]) + # set default verbose options, overridden by following options debug_usb_io=no debug_usb_comms=no @@ -787,6 +803,8 @@ AM_CONDITIONAL([INTERNAL_JIMTCL], [test "x$use_internal_jimtcl" = "xyes"]) AM_CONDITIONAL([HAVE_JIMTCL_PKG_CONFIG], [test "x$have_jimtcl_pkg_config" = "xyes"]) AM_CONDITIONAL([INTERNAL_LIBJAYLINK], [test "x$use_internal_libjaylink" = "xyes"]) +AM_CONDITIONAL([USE_GCOV], [test "x$enable_gcov" = "xyes"]) + # Look for environ alternatives. Possibility #1: is environ in unistd.h or stdlib.h? AC_MSG_CHECKING([for environ in unistd.h and stdlib.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @@ -862,7 +880,8 @@ m4_foreach([adapter], [USB1_ADAPTERS, LIBGPIOD_ADAPTERS, LIBJAYLINK_ADAPTERS, PCIE_ADAPTERS, SERIAL_PORT_ADAPTERS, DUMMY_ADAPTER, - OPTIONAL_LIBRARIES], + OPTIONAL_LIBRARIES, + COVERAGE], [s=m4_format(["%-40s"], ADAPTER_DESC([adapter])) AS_CASE([$ADAPTER_VAR([adapter])], [auto], [ diff --git a/src/openocd.c b/src/openocd.c index 7a5147050..9fd709e32 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -375,6 +375,13 @@ int openocd_main(int argc, char *argv[]) log_exit(); +#if USE_GCOV + /* Always explicitly dump coverage data before terminating. + * Otherwise coverage would not be dumped when exit_on_signal occurs. */ + void __gcov_dump(void); + __gcov_dump(); +#endif + if (ret == ERROR_FAIL) return EXIT_FAILURE; else if (ret != ERROR_OK)