code coverage patch - Mailing list pgsql-hackers

From Michelle Caisse
Subject code coverage patch
Date
Msg-id 48B5E244.80208@sun.com
Whole thread Raw
Responses Re: code coverage patch  (Peter Eisentraut <peter_e@gmx.net>)
Re: code coverage patch  (Peter Eisentraut <peter_e@gmx.net>)
Re: code coverage patch  (Alvaro Herrera <alvherre@commandprompt.com>)
List pgsql-hackers
I've attached a patch that allows the generation of code coverage
statistics. To test it, apply the patch, then:

autoconf
./configure --enable-coverage
make
make check (or execute any other application against the database to see
the coverage of that app)
make coverage
make coverage_out

You will find a coverage directory in your build directory. Open
coverage/index.html to see the code coverage.

There are a couple of issues.

gcov gets confused when source files are generated. I eliminated
src/backend/bootstrap and ../parser from coverage analysis to avoid
errors of this type. With a vpath build only, code coverage fails on
another generated .c file, src/backend/utils/misc/guc-file.c.

Also, two tests fail with the following diff when the build is
configured with --enable-coverage.
       RETURNS trigger
       AS
'/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/refint.so'
       LANGUAGE C;
+ ERROR:  could not load library
"/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/refint.so":
/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/refint.so:
undefined symbol: __gcov_merge_add
 CREATE FUNCTION check_foreign_key ()
       RETURNS trigger
       AS
'/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/refint.so'
       LANGUAGE C;
+ ERROR:  could not load library
"/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/refint.so":
/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/refint.so:
undefined symbol: __gcov_merge_add
 CREATE FUNCTION autoinc ()
       RETURNS trigger
       AS
'/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/autoinc.so'
       LANGUAGE C;
+ ERROR:  could not load library
"/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/autoinc.so":
/home/michelle/trunkClean/pgsql/src/test/regress/../../../contrib/spi/autoinc.so:
undefined symbol: __gcov_merge_add
 CREATE FUNCTION funny_dup17 ()
         RETURNS trigger
         AS '/home/michelle/trunkClean/pgsql/src/test/regress/regress.so'

Please test and comment.

-- Michelle

--
Michelle Caisse               Sun Microsystems
California, U.S.     http://sun.com/postgresql


Index: GNUmakefile.in
===================================================================
RCS file: /projects/cvsroot/pgsql/GNUmakefile.in,v
retrieving revision 1.47
diff -u -r1.47 GNUmakefile.in
--- GNUmakefile.in    18 Mar 2008 16:24:50 -0000    1.47
+++ GNUmakefile.in    27 Aug 2008 20:43:47 -0000
@@ -63,6 +63,25 @@

 ##########################################################################

+coverage: all
+    rm -rf "$(top_builddir)/coverage"
+    mkdir "$(top_builddir)/coverage"
+    $(MAKE) -C src/backend $@
+
+TRACEFILES = $(shell find src/backend -name lcov.info)
+
+coverage_out: coverage
+    $(GENHTML) --show-details --legend --output-directory=coverage --title=PostgreSQL --num-spaces=4 $(TRACEFILES)
+    cd coverage && cp index.html save.html && sed -e '/Date:/ i\
+          <td class="headerItem" width="20%">gcov output:</td> \
+          <td class="headerValue" width="80%" colspan=4><a href="gcov.out">gcov.out</a></td> \
+        </tr> \
+    <tr> ' index.html > tmp.html && mv tmp.html index.html
+    tar cf coverage.tar coverage
+
+
+##########################################################################
+
 distdir    = postgresql-$(VERSION)
 dummy    = =install=
 garbage = =*  "#"*  ."#"*  *~*  *.orig  *.rej  core  postgresql-*
Index: configure
===================================================================
RCS file: /projects/cvsroot/pgsql/configure,v
retrieving revision 1.602
diff -u -r1.602 configure
--- configure    21 Aug 2008 13:53:27 -0000    1.602
+++ configure    27 Aug 2008 20:44:18 -0000
@@ -672,6 +672,10 @@
 enable_rpath
 enable_debug
 enable_profiling
+GCOV
+LCOV
+GENHTML
+enable_coverage
 DTRACE
 DTRACEFLAGS
 enable_dtrace
@@ -1356,6 +1360,7 @@
   --disable-spinlocks     do not use spinlocks
   --enable-debug          build with debugging symbols (-g)
   --enable-profiling      build with profiling enabled
+  --enable-coverage       build with coverage enabled
   --enable-dtrace         build with DTrace support
   --enable-depend         turn on automatic dependency tracking
   --enable-cassert        enable assertion checks (for debugging)
@@ -2469,6 +2474,178 @@


 #
+# --enable-coverage enables generation of code coverage metrics with gcov
+#
+
+pgac_args="$pgac_args enable_coverage"
+
+# Check whether --enable-coverage was given.
+if test "${enable_coverage+set}" = set; then
+  enableval=$enable_coverage;
+  case $enableval in
+    yes)
+      :
+      ;;
+    no)
+      :
+      ;;
+    *)
+      { { echo "$as_me:$LINENO: error: no argument expected for --enable-coverage option" >&5
+echo "$as_me: error: no argument expected for --enable-coverage option" >&2;}
+   { (exit 1); exit 1; }; }
+      ;;
+  esac
+
+else
+  enable_coverage=no
+
+fi
+
+
+for ac_prog in gcov
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_GCOV+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$GCOV"; then
+  ac_cv_prog_GCOV="$GCOV" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_GCOV="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+GCOV=$ac_cv_prog_GCOV
+if test -n "$GCOV"; then
+  { echo "$as_me:$LINENO: result: $GCOV" >&5
+echo "${ECHO_T}$GCOV" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$GCOV" && break
+done
+
+if test -z "$GCOV"; then
+  { { echo "$as_me:$LINENO: error: gcov not found" >&5
+echo "$as_me: error: gcov not found" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+for ac_prog in lcov
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_LCOV+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$LCOV"; then
+  ac_cv_prog_LCOV="$LCOV" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_LCOV="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LCOV=$ac_cv_prog_LCOV
+if test -n "$LCOV"; then
+  { echo "$as_me:$LINENO: result: $LCOV" >&5
+echo "${ECHO_T}$LCOV" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$LCOV" && break
+done
+
+if test -z "$LCOV"; then
+  { { echo "$as_me:$LINENO: error: lcov not found" >&5
+echo "$as_me: error: lcov not found" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+for ac_prog in genhtml
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_GENHTML+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$GENHTML"; then
+  ac_cv_prog_GENHTML="$GENHTML" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_GENHTML="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+GENHTML=$ac_cv_prog_GENHTML
+if test -n "$GENHTML"; then
+  { echo "$as_me:$LINENO: result: $GENHTML" >&5
+echo "${ECHO_T}$GENHTML" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$GENHTML" && break
+done
+
+if test -z "$GENHTML"; then
+  { { echo "$as_me:$LINENO: error: genhtml not found" >&5
+echo "$as_me: error: genhtml not found" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+#
 # DTrace
 #

@@ -3961,6 +4138,17 @@
   CFLAGS="$CFLAGS -g"
 fi

+# enable code coverage if --enable-coverage
+if test "$enable_coverage" = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"
+  else
+    { { echo "$as_me:$LINENO: error: --enable-coverage is supported only when using GCC" >&5
+echo "$as_me: error: --enable-coverage is supported only when using GCC" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+fi
+
 # enable profiling if --enable-profiling
 if test "$enable_profiling" = yes && test "$ac_cv_prog_cc_g" = yes; then
   if test "$GCC" = yes; then
@@ -26513,6 +26701,10 @@
 enable_rpath!$enable_rpath$ac_delim
 enable_debug!$enable_debug$ac_delim
 enable_profiling!$enable_profiling$ac_delim
+GCOV!$GCOV$ac_delim
+LCOV!$LCOV$ac_delim
+GENHTML!$GENHTML$ac_delim
+enable_coverage!$enable_coverage$ac_delim
 DTRACE!$DTRACE$ac_delim
 DTRACEFLAGS!$DTRACEFLAGS$ac_delim
 enable_dtrace!$enable_dtrace$ac_delim
@@ -26552,10 +26744,6 @@
 LD!$LD$ac_delim
 with_gnu_ld!$with_gnu_ld$ac_delim
 ld_R_works!$ld_R_works$ac_delim
-RANLIB!$RANLIB$ac_delim
-STRIP!$STRIP$ac_delim
-STRIP_STATIC_LIB!$STRIP_STATIC_LIB$ac_delim
-STRIP_SHARED_LIB!$STRIP_SHARED_LIB$ac_delim
 _ACEOF

   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -26597,6 +26785,10 @@
 ac_delim='%!_!# '
 for ac_last_try in false false false false false :; do
   cat >conf$$subs.sed <<_ACEOF
+RANLIB!$RANLIB$ac_delim
+STRIP!$STRIP$ac_delim
+STRIP_STATIC_LIB!$STRIP_STATIC_LIB$ac_delim
+STRIP_SHARED_LIB!$STRIP_SHARED_LIB$ac_delim
 TAR!$TAR$ac_delim
 LN_S!$LN_S$ac_delim
 AWK!$AWK$ac_delim
@@ -26647,7 +26839,7 @@
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF

-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 48; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 52; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
Index: configure.in
===================================================================
RCS file: /projects/cvsroot/pgsql/configure.in,v
retrieving revision 1.564
diff -u -r1.564 configure.in
--- configure.in    19 Aug 2008 19:17:40 -0000    1.564
+++ configure.in    27 Aug 2008 20:44:21 -0000
@@ -204,6 +204,25 @@
 AC_SUBST(enable_profiling)

 #
+# --enable-coverage enables generation of code coverage metrics with gcov
+#
+PGAC_ARG_BOOL(enable, coverage, no,
+              [  --enable-coverage       build with coverage enabled])
+AC_CHECK_PROGS(GCOV, gcov)
+if test -z "$GCOV"; then
+  AC_MSG_ERROR([gcov not found])
+fi
+AC_CHECK_PROGS(LCOV, lcov)
+if test -z "$LCOV"; then
+  AC_MSG_ERROR([lcov not found])
+fi
+AC_CHECK_PROGS(GENHTML, genhtml)
+if test -z "$GENHTML"; then
+  AC_MSG_ERROR([genhtml not found])
+fi
+AC_SUBST(enable_coverage)
+
+#
 # DTrace
 #
 PGAC_ARG_BOOL(enable, dtrace, no,
@@ -415,6 +434,15 @@
   CFLAGS="$CFLAGS -g"
 fi

+# enable code coverage if --enable-coverage
+if test "$enable_coverage" = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"
+  else
+    AC_MSG_ERROR([--enable-coverage is supported only when using GCC])
+  fi
+fi
+
 # enable profiling if --enable-profiling
 if test "$enable_profiling" = yes && test "$ac_cv_prog_cc_g" = yes; then
   if test "$GCC" = yes; then
Index: src/Makefile.global.in
===================================================================
RCS file: /projects/cvsroot/pgsql/src/Makefile.global.in,v
retrieving revision 1.241
diff -u -r1.241 Makefile.global.in
--- src/Makefile.global.in    17 Feb 2008 16:36:43 -0000    1.241
+++ src/Makefile.global.in    27 Aug 2008 20:44:23 -0000
@@ -18,7 +18,7 @@
 #
 # Meta configuration

-.PHONY: all install install-strip installdirs uninstall clean distclean maintainer-clean distprep check installcheck
maintainer-check
+.PHONY: all install install-strip installdirs uninstall clean distclean maintainer-clean distprep check installcheck
maintainer-checkcoverage 
 .SILENT: installdirs

 # make `all' the default target
@@ -291,6 +291,12 @@
 NSGMLS    = @NSGMLS@
 SGMLSPL    = @SGMLSPL@

+# Code coverage
+
+GCOV = @GCOV@
+LCOV = @LCOV@
+GENHTML = @GENHTML@
+
 # Feature settings

 DEF_PGPORT = @default_port@
Index: src/backend/common.mk
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/common.mk,v
retrieving revision 1.7
diff -u -r1.7 common.mk
--- src/backend/common.mk    17 Mar 2008 18:24:56 -0000    1.7
+++ src/backend/common.mk    27 Aug 2008 20:44:23 -0000
@@ -18,6 +18,10 @@

 SUBDIROBJS = $(SUBDIRS:%=%/$(subsysfilename))

+SRC = $(OBJS:.o=.c)
+GCDA = $(OBJS:.o=.gcda)
+GCNO = $(OBJS:.o=.gcno)
+
 # top-level backend directory obviously has its own "all" target
 ifneq ($(subdir), src/backend)
 all: $(subsysfilename)
@@ -46,3 +50,15 @@
     for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean || exit; done
 endif
     rm -f $(subsysfilename) $(OBJS)
+ifeq ($(enable_coverage),yes)
+    rm -f $(GCDA) $(GCNO) lcov.info *.gcov
+endif
+
+coverage:
+ifdef SUBDIRS
+    for dir in $(SUBDIRS); do $(MAKE) -C $$dir coverage || exit; done
+endif
+ifneq (,$(SRC))
+    $(GCOV) -b -f -p -o . $(GCOVFLAGS) $(SRC) >>$(top_builddir)/coverage/gcov.out
+    $(LCOV) --directory . --capture --output-file lcov.info $(LCOVFLAGS)
+endif
Index: src/backend/bootstrap/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/bootstrap/Makefile,v
retrieving revision 1.36
diff -u -r1.36 Makefile
--- src/backend/bootstrap/Makefile    19 Feb 2008 10:30:07 -0000    1.36
+++ src/backend/bootstrap/Makefile    27 Aug 2008 20:44:24 -0000
@@ -49,3 +49,9 @@
 clean:
 # And the garbage that might have been left behind by partial build:
     @rm -f y.tab.h y.tab.c y.output lex.yy.c
+
+# Override target in common.mk. gcov gets confused with #line
+# directives indicating source files that don't exist on disk.
+# FIXME
+coverage:
+    # do nothing
Index: src/backend/parser/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/Makefile,v
retrieving revision 1.46
diff -u -r1.46 Makefile
--- src/backend/parser/Makefile    19 Feb 2008 10:30:07 -0000    1.46
+++ src/backend/parser/Makefile    27 Aug 2008 20:44:24 -0000
@@ -61,3 +61,9 @@
 clean:
 # And the garbage that might have been left behind by partial build:
     @rm -f y.tab.h y.tab.c y.output lex.yy.c
+
+# Override target in common.mk. gcov gets confused with #line
+# directives indicating source files that don't exist on disk.
+# FIXME
+coverage:
+    # do nothing

pgsql-hackers by date:

Previous
From: "Greg Stark"
Date:
Subject: Re: Is it really such a good thing for newNode() to be a macro?
Next
From: ITAGAKI Takahiro
Date:
Subject: Re: Auto-explain patch