Import compiler.m4 and lcov.am from telepathy-glib, and use them to replace gcov
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Wed, 2 Feb 2011 16:51:30 +0000 (16:51 +0000)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Thu, 17 Feb 2011 17:07:12 +0000 (17:07 +0000)
Reviewed-by: Colin Walters <walters@verbum.org>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=10887

.gitignore
HACKING
Makefile.am
README
cmake/test/CMakeLists.txt
configure.in
m4/compiler.m4 [new file with mode: 0644]
test/Makefile.am
test/decode-gcov.c [deleted file]
tools/lcov.am [new file with mode: 0644]

index d5e84b7..db3db97 100644 (file)
@@ -33,6 +33,11 @@ config.h.in
 *.bbg
 *.da
 *.gcov
+*.gcda
+*.gcno
 tags
 /dbus-1.*/
 /dbus-1.*.tar.*
+/lcov.html/
+/lcov.info
+/lcov.info.tmp
diff --git a/HACKING b/HACKING
index 442508a..2e41715 100644 (file)
--- a/HACKING
+++ b/HACKING
@@ -289,10 +289,8 @@ is to add a test in here.
 
 "make check" runs all the deterministic test programs (i.e. not break-loader).
 
-"make check-coverage" is available if you configure with --enable-gcov and 
-gives a complete report on test suite coverage. You can also run 
-"test/decode-gcov foo.c" on any source file to get annotated source, 
-after running make check with a gcov-enabled tree.
+"make lcov-check" is available if you configure with --enable-compiler-coverage
+and gives a complete report on test suite coverage.
 
 Patches
 ===
index 2ce25d5..ac7dcb1 100644 (file)
@@ -23,46 +23,6 @@ EXTRA_DIST =                 \
 
 all-local: Doxyfile
 
-if DBUS_GCOV_ENABLED
-clean-gcov:
-       find -name "*.da" -o -name "*.gcov" | xargs rm || true
-
-clean-bbg:
-       find -name "*.bbg" -o -name "*.bb" | xargs rm || true
-
-GCOV_DIRS=dbus bus
-
-## .PHONY so it always rebuilds it
-.PHONY: coverage-report.txt
-coverage-report.txt:
-       BBG_FILES=`find $(GCOV_DIRS) -name "*.bbg" -o -name "*.gcno"` ;                 \
-       C_FILES= ;                                                                      \
-       for F in $$BBG_FILES ; do                                                       \
-               F_nolibs=`echo $$F | sed -e 's/.libs\///g'` ;                           \
-               C=`echo $$F_nolibs | sed -e 's/.bbg/.c/g' | sed -e 's/.gcno/.c/g'`  ;   \
-               B=`basename $$F .bbg` ;                                                 \
-               D=`dirname $$F` ;                                                       \
-               DA=`echo $$F | sed -e 's/.bbg/.da/g'` ;                                 \
-               DA_libs=`echo $$D/.libs/$$B/.da` ;                                      \
-               if test -e $$DA || test -e $$DA_libs; then                              \
-                       C_FILES="$$C_FILES $$C" ;                                       \
-               fi ;                                                                    \
-       done ;                                                                          \
-       echo $$C_FILES ;                                                                \
-       $(top_builddir)/test/decode-gcov --report $$C_FILES > coverage-report.txt
-
-check-coverage: clean-gcov all check coverage-report.txt
-       cat coverage-report.txt
-
-else
-coverage-report.txt:
-       echo "Need to reconfigure with --enable-gcov"
-
-check-coverage:
-       echo "Need to reconfigure with --enable-gcov"
-
-endif
-
 update-authors:
        git shortlog -s -e | cut -c 8- | sort > AUTHORS
 
@@ -70,3 +30,5 @@ DISTCHECK_CONFIGURE_FLAGS = \
        --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 
 ACLOCAL_AMFLAGS = -I m4
+
+include tools/lcov.am
diff --git a/README b/README
index 89bfaf7..be4aede 100644 (file)
--- a/README
+++ b/README
@@ -83,7 +83,7 @@ the ./configure program are these
   --enable-checks            include sanity checks on public API
   --enable-xml-docs          build XML documentation (requires xmlto)
   --enable-doxygen-docs      build DOXYGEN documentation (requires Doxygen)
-  --enable-gcov              compile with coverage profiling instrumentation (gcc only)
+  --enable-compiler-coverage compile with coverage profiling instrumentation (gcc only)
   --enable-abstract-sockets  use abstract socket namespace (linux only)
   --enable-selinux           build with SELinux support
   --enable-dnotify           build with dnotify support (linux only)
index 7bb80ab..9a259d2 100644 (file)
@@ -52,10 +52,6 @@ set (test-sleep-forever_SOURCES
     ${CMAKE_SOURCE_DIR}/../test/test-sleep-forever.c
 )
 
-set (decode_gcov_SOURCES
-    ${CMAKE_SOURCE_DIR}/../test/decode-gcov.c
-)
-
 add_executable(test-service ${test-service_SOURCES})
 target_link_libraries(test-service ${DBUS_INTERNAL_LIBRARIES})
 
@@ -81,9 +77,6 @@ target_link_libraries(test-segfault ${DBUS_INTERNAL_LIBRARIES})
 add_executable(test-sleep-forever ${test-sleep-forever_SOURCES})
 target_link_libraries(test-sleep-forever ${DBUS_INTERNAL_LIBRARIES})
 
-#add_executable(decode-gcov ${decode_gcov_SOURCES})
-#target_link_libraries(decode-gcov ${DBUS_INTERNAL_LIBRARIES})
-
 ### keep these in creation order, i.e. uppermost dirs first 
 set (TESTDIRS
     test/data
index 2b1929e..9485ebb 100644 (file)
@@ -71,6 +71,8 @@ AC_HEADER_STDC
 AC_C_INLINE
 AM_PROG_LIBTOOL
 AC_PROG_MKDIR_P
+COMPILER_COVERAGE
+COMPILER_OPTIMISATIONS
 
 # Set some internal variables depending on the platform for later use.
 dbus_win=no
@@ -128,7 +130,6 @@ AC_ARG_ENABLE(asserts, AS_HELP_STRING([--enable-asserts],[include assertion chec
 AC_ARG_ENABLE(checks, AS_HELP_STRING([--enable-checks],[include sanity checks on public API]),enable_checks=$enableval,enable_checks=yes)
 AC_ARG_ENABLE(xml-docs, AS_HELP_STRING([--enable-xml-docs],[build XML documentation (requires xmlto)]),enable_xml_docs=$enableval,enable_xml_docs=auto)
 AC_ARG_ENABLE(doxygen-docs, AS_HELP_STRING([--enable-doxygen-docs],[build DOXYGEN documentation (requires Doxygen)]),enable_doxygen_docs=$enableval,enable_doxygen_docs=auto)
-AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov],[compile with coverage profiling instrumentation (gcc only)]),enable_gcov=$enableval,enable_gcov=no)
 AC_ARG_ENABLE(abstract-sockets, AS_HELP_STRING([--enable-abstract-sockets],[use abstract socket namespace (linux only)]),enable_abstract_sockets=$enableval,enable_abstract_sockets=auto)
 AC_ARG_ENABLE(selinux, AS_HELP_STRING([--enable-selinux],[build with SELinux support]),enable_selinux=$enableval,enable_selinux=auto)
 AC_ARG_ENABLE(libaudit,AS_HELP_STRING([--enable-libaudit],[build audit daemon support for SELinux]),enable_libaudit=$enableval,enable_libaudit=auto)
@@ -202,20 +203,10 @@ if test x$enable_userdb_cache = xyes; then
     AC_DEFINE(DBUS_ENABLE_USERDB_CACHE,1,[Build with caching of user data])
 fi
 
-if test x$enable_gcov = xyes; then
+if test x$enable_compiler_coverage = xyes; then
      ## so that config.h changes when you toggle gcov support
      AC_DEFINE_UNQUOTED(DBUS_GCOV_ENABLED, 1, [Defined if gcov is enabled to force a rebuild due to config.h changing])
-
-     AC_MSG_CHECKING([for gcc 3.3 version of gcov file format])
-     have_gcc33_gcov=no
-     AC_RUN_IFELSE( [AC_LANG_PROGRAM( , [[ if (__GNUC__ >=3 && __GNUC_MINOR__ >= 3) exit (0); else exit (1); ]])],
-                   have_gcc33_gcov=yes)
-     if test x$have_gcc33_gcov = xyes ; then
-         AC_DEFINE_UNQUOTED(DBUS_HAVE_GCC33_GCOV, 1, [Defined if we have gcc 3.3 and thus the new gcov format])
-     fi
-     AC_MSG_RESULT($have_gcc33_gcov)
 fi
-AM_CONDITIONAL(DBUS_GCOV_ENABLED, test x$enable_gcov = xyes)
 
 # glibc21.m4 serial 3
 dnl Copyright (C) 2000-2002, 2004 Free Software Foundation, Inc.
@@ -1295,24 +1286,7 @@ if test "x$GCC" = "xyes"; then
     *) CFLAGS="$CFLAGS -pedantic" ;;
     esac
   fi
-  if test x$enable_gcov = xyes; then
-    case " $CFLAGS " in
-    *[\ \      ]-fprofile-arcs[\ \     ]*) ;;
-    *) CFLAGS="$CFLAGS -fprofile-arcs" ;;
-    esac
-    case " $CFLAGS " in
-    *[\ \      ]-ftest-coverage[\ \    ]*) ;;
-    *) CFLAGS="$CFLAGS -ftest-coverage" ;;
-    esac
-
-    ## remove optimization
-    CFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9]*//g'`
-  fi
   changequote([,])dnl
-else
-  if test x$enable_gcov = xyes; then
-    AC_MSG_ERROR([--enable-gcov can only be used with gcc])
-  fi
 fi
 
 AC_SUBST(PIC_CFLAGS)
@@ -1704,7 +1678,7 @@ echo "
 
 echo "
         Maintainer mode:          ${USE_MAINTAINER_MODE}
-        gcc coverage profiling:   ${enable_gcov}
+        gcc coverage profiling:   ${enable_compiler_coverage}
         Building unit tests:      ${enable_tests}
         Building verbose mode:    ${enable_verbose_mode}
         Building assertions:      ${enable_asserts}
@@ -1743,7 +1717,7 @@ fi
 if test x$enable_tests = xyes -a x$enable_asserts = xno; then
         echo "NOTE: building with unit tests but without assertions means tests may not properly report failures (this configuration is only useful when doing something like profiling the tests)"
 fi
-if test x$enable_gcov = xyes; then
+if test x$enable_compiler_coverage = xyes; then
         echo "NOTE: building with coverage profiling is definitely for developers only."
 fi
 if test x$enable_verbose_mode = xyes; then
diff --git a/m4/compiler.m4 b/m4/compiler.m4
new file mode 100644 (file)
index 0000000..5aff5d8
--- /dev/null
@@ -0,0 +1,67 @@
+# compiler.m4 - autoconf macros for compiler settings
+#
+# Copyright © 2005 Scott James Remnant <scott@netsplit.com>.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+# COMPILER_WARNINGS
+# ----------------------
+# Add configure option to enable additional compiler warnings and treat
+# them as errors.
+AC_DEFUN([COMPILER_WARNINGS],
+[AC_ARG_ENABLE(compiler-warnings,
+       AS_HELP_STRING([--enable-compiler-warnings],
+                      [Enable additional compiler warnings]),
+[if test "x$enable_compiler_warnings" = "xyes"; then
+       if test "x$GCC" = "xyes"; then
+                CFLAGS="-Wall -Werror $CFLAGS"
+        fi
+       if test "x$GXX" = "xyes"; then
+               CXXFLAGS="-Wall -Werror $CXXFLAGS"
+       fi
+fi])dnl
+])# COMPILER_WARNINGS
+
+# COMPILER_OPTIMISATIONS
+# ---------------------------
+# Add configure option to disable optimisations.
+AC_DEFUN([COMPILER_OPTIMISATIONS],
+[AC_ARG_ENABLE(compiler-optimisations,
+       AS_HELP_STRING([--disable-compiler-optimisations],
+                      [Disable compiler optimisations]),
+[if test "x$enable_compiler_optimisations" = "xno"; then
+       [CFLAGS=`echo "$CFLAGS" | sed -e "s/ -O[1-9]*\b/ -O0/g"`]
+fi])dnl
+])# COMPILER_OPTIMISATIONS
+
+# COMPILER_COVERAGE
+# ----------------------
+# Add configure option to enable coverage data.
+AC_DEFUN([COMPILER_COVERAGE],
+[AC_ARG_ENABLE(compiler-coverage,
+       AS_HELP_STRING([--enable-compiler-coverage],
+                      [Enable generation of coverage data]),
+[if test "x$enable_compiler_coverage" = "xyes"; then
+       if test "x$GCC" = "xyes"; then
+               CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"
+       fi
+fi])dnl
+])# COMPILER_COVERAGE
index 110e5dc..9d0681b 100644 (file)
@@ -23,13 +23,7 @@ TEST_BINARIES=
 TESTS=
 endif
 
-if DBUS_GCOV_ENABLED
-GCOV_BINARIES=decode-gcov
-else
-GCOV_BINARIES=
-endif
-
-noinst_PROGRAMS= $(TEST_BINARIES) $(GCOV_BINARIES)
+noinst_PROGRAMS= $(TEST_BINARIES)
 
 test_service_SOURCES=                          \
        test-service.c
@@ -58,9 +52,6 @@ test_segfault_SOURCES =                               \
 test_sleep_forever_SOURCES =                   \
        test-sleep-forever.c
 
-decode_gcov_SOURCES=                           \
-       decode-gcov.c
-
 # When any programs are not linked to libdbus-internal, fix this.
 AM_CPPFLAGS=-DDBUS_STATIC_BUILD
 TEST_LIBS=$(top_builddir)/dbus/libdbus-internal.la $(DBUS_TEST_LIBS)
@@ -77,8 +68,6 @@ shell_test_LDADD=libdbus-testutils.la $(TEST_LIBS)
 shell_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
 spawn_test_LDADD=$(TEST_LIBS)
 spawn_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
-decode_gcov_LDADD=$(TEST_LIBS)
-decode_gcov_LDFLAGS=@R_DYNAMIC_LDFLAG@
 
 EXTRA_DIST=
 
diff --git a/test/decode-gcov.c b/test/decode-gcov.c
deleted file mode 100644 (file)
index 3b2a152..0000000
+++ /dev/null
@@ -1,2653 +0,0 @@
- /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/* decode-gcov.c gcov decoder program
- *
- * Copyright (C) 2003  Red Hat Inc.
- *
- * Partially derived from gcov,
- * Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
- * 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
- *
- * This file is NOT licensed under the Academic Free License
- * as it is largely derived from gcov.c and gcov-io.h in the
- * gcc source code.
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#include <config.h>
-#define DBUS_COMPILATION /* cheat */
-#include <dbus/dbus-list.h>
-#include <dbus/dbus-string.h>
-#include <dbus/dbus-sysdeps.h>
-#include <dbus/dbus-marshal.h>
-#include <dbus/dbus-hash.h>
-#undef DBUS_COMPILATION
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef DBUS_HAVE_INT64
-#error "gcov support can't be built without 64-bit integer support"
-#endif
-
-static void
-die (const char *message)
-{
-  fprintf (stderr, "%s", message);
-  exit (1);
-}
-
-/* This bizarro function is from gcov-io.h in gcc source tree */
-static int
-fetch_long (long        *dest,
-            const char  *source,
-            size_t       bytes)
-{
-  long value = 0;
-  int i;
-                                                                                
-  for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
-    if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
-      return 1;
-                                                                                
-  for (; i >= 0; i--)
-    value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
-                                                                                
-  if ((source[bytes - 1] & 128) && (value > 0))
-    value = - value;
-                                                                                
-  *dest = value;
-  return 0;
-}
-
-static int
-fetch_long64 (dbus_int64_t *dest,
-              const char   *source,
-              size_t        bytes)
-{
-  dbus_int64_t value = 0;
-  int i;
-                                                                                
-  for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
-    if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
-      return 1;
-                                                                                
-  for (; i >= 0; i--)
-    value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
-                                                                                
-  if ((source[bytes - 1] & 128) && (value > 0))
-    value = - value;
-                                                                                
-  *dest = value;
-  return 0;
-}
-
-#define BB_FILENAME    (-1)
-#define BB_FUNCTION    (-2)
-#define BB_ENDOFLIST   0
-
-static dbus_bool_t
-string_get_int (const DBusString *str,
-                int               start,
-                long             *val)
-{
-  const char *p;
-  
-  if ((_dbus_string_get_length (str) - start) < 4)
-    return FALSE;
-
-  p = _dbus_string_get_const_data (str);
-
-  p += start;
-
-  fetch_long (val, p, 4);
-  
-  return TRUE;
-}
-
-static dbus_bool_t
-string_get_int64 (const DBusString *str,
-                  int               start,
-                  dbus_int64_t     *val)
-{
-  const char *p;
-  
-  if ((_dbus_string_get_length (str) - start) < 8)
-    return FALSE;
-
-  p = _dbus_string_get_const_data (str);
-
-  p += start;
-
-  fetch_long64 (val, p, 8);
-  
-  return TRUE;
-}
-
-static dbus_bool_t
-string_get_string (const DBusString *str,
-                   int               start,
-                   long              terminator,
-                   DBusString       *val,
-                   int              *end)
-{
-  int i;
-  long n;
-  
-  i = start;
-  while (string_get_int (str, i, &n))
-    {
-      unsigned char b;
-      
-      i += 4;
-      
-      if (n == terminator)
-        break;
-
-      b = n & 0xff;
-      if (b)
-        {
-          _dbus_string_append_byte (val, b);
-          b = (n >> 8) & 0xff;
-          if (b)
-            {
-              _dbus_string_append_byte (val, b);
-              b = (n >> 16) & 0xff;
-              if (b)
-                {
-                  _dbus_string_append_byte (val, b);
-                  b = (n >> 24) & 0xff;
-                  if (b)
-                    _dbus_string_append_byte (val, b);
-                }
-            }
-        }
-    }
-
-  *end = i;
-  
-  return TRUE;
-}
-
-#ifdef DBUS_HAVE_GCC33_GCOV
-/* In gcc33 .bbg files, there's a function name of the form:
- *   -1, length, name (padded to 4), -1, checksum
- */
-static dbus_bool_t
-string_get_function (const DBusString *str,
-                     int               start,
-                     DBusString       *funcname,
-                     int              *checksum,
-                     int              *next)
-{
-  int end;
-  long val;
-  int i;
-
-  i = start;
-  
-  if (!string_get_int (str, i, &val))
-    die ("no room for -1 before function name\n");
-        
-  i += 4;
-
-  if (val != -1)
-    die ("value before function name is not -1\n");
-  
-  if (!string_get_int (str, i, &val))
-    die ("no length found for function name\n");
-        
-  i += 4;
-
-  end = i + val;
-  if (end > _dbus_string_get_length (str))
-    die ("Function name length points past end of file\n");
-
-  if (!_dbus_string_append (funcname,
-                            _dbus_string_get_const_data (str) + i))
-    die ("no memory\n");
-        
-  /* skip alignment padding the length doesn't include the nul so add 1
-   */
-  i = _DBUS_ALIGN_VALUE (end + 1, 4);
-        
-  if (!string_get_int (str, i, &val) ||
-      val != -1)
-    die ("-1 at end of function name not found\n");
-        
-  i += 4;
-
-  if (!string_get_int (str, i, &val))
-    die ("no checksum found at end of function name\n");
-        
-  i += 4;
-
-  *checksum = val;
-
-  *next = i;
-
-  return TRUE;
-}
-#endif /* DBUS_HAVE_GCC33_GCOV */
-
-static void
-dump_bb_file (const DBusString *contents)
-{
-  int i;
-  long val;
-  int n_functions;
-
-  n_functions = 0;
-  i = 0;
-  while (string_get_int (contents, i, &val))
-    {
-      i += 4;
-      
-      switch (val)
-        {
-        case BB_FILENAME:
-          {
-            DBusString f;
-
-            if (!_dbus_string_init (&f))
-              die ("no memory\n");
-
-            if (string_get_string (contents, i,
-                                   BB_FILENAME,
-                                   &f, &i))
-              {
-                printf ("File %s\n", _dbus_string_get_const_data (&f));
-              }
-            _dbus_string_free (&f);
-          }
-          break;
-        case BB_FUNCTION:
-          {
-            DBusString f;
-            if (!_dbus_string_init (&f))
-              die ("no memory\n");
-
-            if (string_get_string (contents, i,
-                                   BB_FUNCTION,
-                                   &f, &i))
-              {
-                printf ("Function %s\n", _dbus_string_get_const_data (&f));
-              }
-            _dbus_string_free (&f);
-
-            n_functions += 1;
-          }
-          break;
-        case BB_ENDOFLIST:
-          printf ("End of block\n");
-          break;
-        default:
-          printf ("Line %ld\n", val);
-          break;
-        }
-    }
-
-  printf ("%d functions in file\n", n_functions);
-}
-
-#define FLAG_ON_TREE 0x1
-#define FLAG_FAKE 0x2
-#define FLAG_FALL_THROUGH 0x4
-
-static void
-dump_bbg_file (const DBusString *contents)
-{
-  int i;
-  long val;
-  int n_functions;
-  int n_arcs;
-  int n_blocks;
-  int n_arcs_off_tree;
-  
-  n_arcs_off_tree = 0;
-  n_blocks = 0;
-  n_arcs = 0;
-  n_functions = 0;
-  i = 0;
-  while (i < _dbus_string_get_length (contents))
-    {
-      long n_blocks_in_func;
-      long n_arcs_in_func; 
-      int j;
-
-#ifdef DBUS_HAVE_GCC33_GCOV
-      /* In gcc33 .bbg files, there's a function name of the form:
-       *   -1, length, name (padded to 4), -1, checksum
-       * after that header on each function description, it's
-       * the same as in gcc32
-       */
-
-      {
-        DBusString funcname;
-        int checksum;
-        
-        if (!_dbus_string_init (&funcname))
-          die ("no memory\n");
-
-        if (!string_get_function (contents, i,
-                                  &funcname, &checksum, &i))
-          die ("could not read function name\n");
-        
-        printf ("Function name is \"%s\" checksum %d\n",
-                _dbus_string_get_const_data (&funcname),
-                checksum);
-        
-        _dbus_string_free (&funcname);
-      }
-#endif /* DBUS_HAVE_GCC33_GCOV */
-      
-      if (!string_get_int (contents, i, &val))
-        die ("no count of blocks in func found\n");
-      
-      i += 4;
-      
-      n_blocks_in_func = val;
-
-      if (!string_get_int (contents, i, &n_arcs_in_func))
-        break;
-
-      i += 4;
-
-      printf ("Function has %ld blocks and %ld arcs\n",
-              n_blocks_in_func, n_arcs_in_func);
-
-      n_functions += 1;
-      n_blocks += n_blocks_in_func;
-      n_arcs += n_arcs_in_func;
-      
-      j = 0;
-      while (j < n_blocks_in_func)
-        {
-          long n_arcs_in_block;
-          int k;
-          
-          if (!string_get_int (contents, i, &n_arcs_in_block))
-            break;
-
-          i += 4;
-
-          printf ("  Block has %ld arcs\n", n_arcs_in_block);
-          
-          k = 0;
-          while (k < n_arcs_in_block)
-            {
-              long destination_block;
-              long flags;
-              
-              if (!string_get_int (contents, i, &destination_block))
-                break;
-
-              i += 4;
-              
-              if (!string_get_int (contents, i, &flags))
-                break;
-
-              i += 4;
-
-              printf ("    Arc has destination block %ld flags 0x%lx\n",
-                      destination_block, flags);
-
-              if ((flags & FLAG_ON_TREE) == 0)
-                n_arcs_off_tree += 1;
-              
-              ++k;
-            }
-
-          if (k < n_arcs_in_block)
-            break;
-          
-          ++j;
-        }
-
-      if (j < n_blocks_in_func)
-        break;
-
-      if (!string_get_int (contents, i, &val))
-        break;
-
-      i += 4;
-
-      if (val != -1)
-        die ("-1 separator not found\n");
-    }
-
-  printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
-          n_functions, n_blocks, n_arcs, n_arcs_off_tree);
-}
-
-#ifndef DBUS_HAVE_GCC33_GCOV
-
-/* gcc 3.2 version:
- * The da file contains first a count of arcs in the file,
- * then a count of executions for all "off tree" arcs
- * in the file.
- */
-static void
-dump_da_file (const DBusString *contents)
-{
-  int i;
-  dbus_int64_t val;
-  int n_arcs;
-  int claimed_n_arcs;
-
-  i = 0;
-  if (!string_get_int64 (contents, i, &val))
-    return;
-
-  i += 8;
-  
-  printf ("%ld arcs in file\n", (long) val);
-  claimed_n_arcs = val;
-  
-  n_arcs = 0;
-  while (string_get_int64 (contents, i, &val))
-    {
-      i += 8;
-
-      printf ("%ld executions of arc %d\n",
-              (long) val, n_arcs);
-
-      ++n_arcs;
-    }
-
-  if (n_arcs != claimed_n_arcs)
-    {
-      printf ("File claimed to have %d arcs but only had %d\n",
-              claimed_n_arcs, n_arcs);
-    }
-}
-
-#else /* DBUS_HAVE_GCC33_GCOV */
-
-/* gcc 3.3 version:
- * The da file is more complex than 3.2.
- *
- * We have a magic value of "-123" only it isn't really
- * -123, it's -123 as encoded by the crackass gcov-io.h
- * routines. Anyway, 4 bytes.
- *
- * We then have:
- *
- *   - 4 byte count of how many functions in the following list
- *   - 4 byte length of random extra data
- *   - the random extra data, just skip it, info pages have some
- *     details on what might be in there or see __bb_exit_func in gcc
- *   - then for each function (number of functions given above):
- *     . -1, length, funcname, alignment padding, -1
- *     . checksum
- *     . 4 byte number of arcs in function
- *     . 8 bytes each, a count of execution for each arc
- *
- * Now, the whole thing *starting with the magic* can repeat.
- * This is caused by multiple runs of the profiled app appending
- * to the file.
- */
-static void
-dump_da_file (const DBusString *contents)
-{
-  int i;
-  dbus_int64_t v64;
-  long val;
-  int n_sections;
-  int total_functions;
-
-  total_functions = 0;
-  n_sections = 0;
-
-  i = 0;
-  while (i < _dbus_string_get_length (contents))
-    {
-      int claimed_n_functions;
-      int n_functions;
-      int total_arcs;
-
-      printf (".da file section %d\n", n_sections);
-      
-      if (!string_get_int (contents, i, &val))
-        die ("no magic found in .da file\n");
-
-      i += 4;
-
-      if (val != -123)
-        die ("wrong file magic in .da file\n");
-
-      if (!string_get_int (contents, i, &val))
-        die ("no function count in .da file\n");
-      i += 4;
-      claimed_n_functions = val;
-
-      printf ("%d functions expected in section %d of .da file\n",
-              claimed_n_functions, n_sections);
-      
-      if (!string_get_int (contents, i, &val))
-        die ("no extra data length in .da file\n");
-
-      i += 4;
-
-      i += val;
-
-      total_arcs = 0;
-      n_functions = 0;
-      while (n_functions < claimed_n_functions)
-        {
-          DBusString funcname;
-          int checksum;
-          int claimed_n_arcs;
-          int n_arcs;
-          
-          if (!_dbus_string_init (&funcname))
-            die ("no memory\n");
-          
-          if (!string_get_function (contents, i,
-                                    &funcname, &checksum, &i))
-            die ("could not read function name\n");
-          
-          if (!string_get_int (contents, i, &val))
-            die ("no arc count for function\n");
-          
-          i += 4;
-          claimed_n_arcs = val;
-          
-          printf ("  %d arcs in function %d %s checksum %d\n",
-                  claimed_n_arcs, n_functions,
-                  _dbus_string_get_const_data (&funcname),
-                  checksum);
-          
-          n_arcs = 0;
-          while (n_arcs < claimed_n_arcs)
-            {
-              if (!string_get_int64 (contents, i, &v64))
-                die ("did not get execution count for arc\n");
-              
-              i += 8;
-              
-              printf ("    %ld executions of arc %d (total arcs %d)\n",
-                      (long) v64, n_arcs, total_arcs + n_arcs);
-              
-              ++n_arcs;
-            }
-
-          _dbus_string_free (&funcname);
-
-          total_arcs += n_arcs;
-          ++n_functions;
-        }
-
-      printf ("total of %d functions and %d arcs in section %d\n",
-              n_functions, total_arcs, n_sections);
-      
-      total_functions += n_functions;
-      ++n_sections;
-    }
-
-  printf ("%d total function sections in %d total .da file sections\n",
-          total_functions, n_sections);
-}
-
-#endif /* DBUS_HAVE_GCC33_GCOV */
-
-typedef struct Arc Arc;
-typedef struct Block Block;
-typedef struct Function Function;
-typedef struct File File;
-typedef struct Line Line;
-
-struct Arc
-{
-  int source;
-  int target;
-  dbus_int64_t arc_count;
-  unsigned int count_valid : 1;
-  unsigned int on_tree : 1;
-  unsigned int fake : 1;
-  unsigned int fall_through : 1;
-  Arc *pred_next;
-  Arc *succ_next;
-};
-
-struct Block
-{
-  Arc *succ;
-  Arc *pred;
-  dbus_int64_t succ_count;
-  dbus_int64_t pred_count;
-  dbus_int64_t exec_count;
-  DBusList *lines;
-  unsigned int count_valid : 1;
-  unsigned int on_tree : 1;
-  unsigned int inside_dbus_build_tests : 1;
-};
-
-struct Function
-{
-  char *name;
-  int checksum;
-  Block *block_graph;
-  int n_blocks;
-  /* number of blocks in DBUS_BUILD_TESTS */
-  int n_test_blocks;
-  int n_test_blocks_executed;
-  /* number of blocks outside DBUS_BUILD_TESTS */
-  int n_nontest_blocks;
-  int n_nontest_blocks_executed;
-  /* Summary result flags */
-  unsigned int unused : 1;
-  unsigned int inside_dbus_build_tests : 1;
-  unsigned int partial : 1; /* only some of the blocks were executed */
-};
-
-struct Line
-{
-  int    number;
-  char  *text;
-  DBusList *blocks;
-  unsigned int inside_dbus_build_tests : 1;
-  unsigned int partial : 1; /* only some of the blocks were executed */
-};
-
-struct File
-{
-  char *name;
-  Line *lines;
-  int   n_lines;
-  DBusList *functions;
-};
-
-static void
-function_add_arc (Function *function,
-                  long      source,
-                  long      target,
-                  long      flags)
-{
-  Arc *arc;
-
-  arc = dbus_new0 (Arc, 1);
-  if (arc == NULL)
-    die ("no memory\n");
-  
-  arc->target = target;
-  arc->source = source;
-
-  arc->succ_next = function->block_graph[source].succ;
-  function->block_graph[source].succ = arc;
-  function->block_graph[source].succ_count += 1;
-
-  arc->pred_next = function->block_graph[target].pred;
-  function->block_graph[target].pred = arc;
-  function->block_graph[target].pred_count += 1;
-
-  if ((flags & FLAG_ON_TREE) != 0)
-    arc->on_tree = TRUE;
-
-  if ((flags & FLAG_FAKE) != 0)
-    arc->fake = TRUE;
-
-  if ((flags & FLAG_FALL_THROUGH) != 0)
-    arc->fall_through = TRUE;
-}
-
-
-static Arc*
-reverse_arcs (Arc *arc)
-{
-  struct Arc *prev = 0;
-  struct Arc *next;
-
-  for ( ; arc; arc = next)
-    {
-      next = arc->succ_next;
-      arc->succ_next = prev;
-      prev = arc;
-    }
-
-  return prev;
-}
-
-static void
-function_reverse_succ_arcs (Function *func)
-{
-  /* Must reverse the order of all succ arcs, to ensure that they match
-   * the order of the data in the .da file.
-   */
-  int i;
-  
-  for (i = 0; i < func->n_blocks; i++)
-    if (func->block_graph[i].succ)
-      func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ);
-}
-
-static void
-get_functions_from_bbg (const DBusString  *contents,
-                        DBusList         **functions)
-{
-  int i;
-  long val;
-  int n_functions;
-  int n_arcs;
-  int n_blocks;
-  int n_arcs_off_tree;
-
-#if 0
-  printf ("Loading arcs and blocks from .bbg file\n");
-#endif
-  
-  n_arcs_off_tree = 0;
-  n_blocks = 0;
-  n_arcs = 0;
-  n_functions = 0;
-  i = 0;
-  while (i < _dbus_string_get_length (contents))
-    {
-      Function *func;
-      long n_blocks_in_func;
-      long n_arcs_in_func; 
-      int j;
-
-#ifdef DBUS_HAVE_GCC33_GCOV
-      DBusString funcname;
-      int checksum;
-
-      /* In gcc33 .bbg files, there's a function name of the form:
-       *   -1, length, name (padded to 4), -1, checksum
-       * after that header on each function description, it's
-       * the same as in gcc32
-       */
-      if (!_dbus_string_init (&funcname))
-        die ("no memory\n");
-      
-      if (!string_get_function (contents, i,
-                                &funcname, &checksum, &i))
-        die ("could not read function name\n");
-#endif /* DBUS_HAVE_GCC33_GCOV */
-
-      if (!string_get_int (contents, i, &val))
-        break;
-      
-      n_blocks_in_func = val;
-      
-      i += 4;
-
-      if (!string_get_int (contents, i, &n_arcs_in_func))
-        break;
-
-      i += 4;
-
-      n_functions += 1;
-      n_blocks += n_blocks_in_func;
-      n_arcs += n_arcs_in_func;
-
-      func = dbus_new0 (Function, 1);
-      if (func == NULL)
-        die ("no memory\n");
-
-#ifdef DBUS_HAVE_GCC33_GCOV
-      func->name = _dbus_strdup (_dbus_string_get_const_data (&funcname));
-      func->checksum = checksum;
-      _dbus_string_free (&funcname);
-#endif
-      
-      func->block_graph = dbus_new0 (Block, n_blocks_in_func);
-      func->n_blocks = n_blocks_in_func;
-      
-      j = 0;
-      while (j < n_blocks_in_func)
-        {
-          long n_arcs_in_block;
-          int k;
-          
-          if (!string_get_int (contents, i, &n_arcs_in_block))
-            break;
-
-          i += 4;
-          
-          k = 0;
-          while (k < n_arcs_in_block)
-            {
-              long destination_block;
-              long flags;
-              
-              if (!string_get_int (contents, i, &destination_block))
-                break;
-
-              i += 4;
-              
-              if (!string_get_int (contents, i, &flags))
-                break;
-
-              i += 4;
-
-              if ((flags & FLAG_ON_TREE) == 0)
-                n_arcs_off_tree += 1;
-
-              function_add_arc (func, j, destination_block,
-                                flags);
-              
-              ++k;
-            }
-
-          if (k < n_arcs_in_block)
-            break;
-          
-          ++j;
-        }
-
-      if (j < n_blocks_in_func)
-        break;
-
-      function_reverse_succ_arcs (func);
-      
-      if (!_dbus_list_append (functions, func))
-        die ("no memory\n");
-      
-      if (!string_get_int (contents, i, &val))
-        break;
-
-      i += 4;
-
-      if (val != -1)
-        die ("-1 separator not found in .bbg file\n");
-    }
-
-#if 0
-  printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
-          n_functions, n_blocks, n_arcs, n_arcs_off_tree);
-#endif
-  
-  _dbus_assert (n_functions == _dbus_list_get_length (functions));
-}
-
-#ifdef DBUS_HAVE_GCC33_GCOV
-static void
-add_counts_from_da (const DBusString  *contents,
-                    DBusList         **functions)
-{
-  int i;
-  dbus_int64_t v64;
-  long val;
-  int n_sections;
-  DBusList *link;
-  Function *current_func;  
-  int current_block;
-  Arc *current_arc;
-
-  n_sections = 0;
-
-  i = 0;
-  while (i < _dbus_string_get_length (contents))
-    {
-      int claimed_n_functions;
-      int n_functions;
-      
-      if (!string_get_int (contents, i, &val))
-        die ("no magic found in .da file\n");
-
-      i += 4;
-
-      if (val != -123)
-        die ("wrong file magic in .da file\n");
-
-      if (!string_get_int (contents, i, &val))
-        die ("no function count in .da file\n");
-      i += 4;
-      claimed_n_functions = val;
-      
-      if (!string_get_int (contents, i, &val))
-        die ("no extra data length in .da file\n");
-
-      i += 4;
-
-      i += val;
-
-      link = _dbus_list_get_first_link (functions);
-      if (link == NULL)
-        goto no_more_functions;
-      
-      n_functions = 0;
-      while (n_functions < claimed_n_functions && link != NULL)
-        {
-          DBusString funcname;
-          int checksum;
-          int claimed_n_arcs;
-          int n_arcs;
-
-          current_func = link->data;
-          current_block = 0;
-          current_arc = current_func->block_graph[current_block].succ;
-          
-          if (!_dbus_string_init (&funcname))
-            die ("no memory\n");
-          
-          if (!string_get_function (contents, i,
-                                    &funcname, &checksum, &i))
-            die ("could not read function name\n");
-
-          if (!_dbus_string_equal_c_str (&funcname, current_func->name))
-            {
-              fprintf (stderr, "Expecting .da info for %s but got %s\n",
-                       current_func->name,
-                       _dbus_string_get_const_data (&funcname));
-              exit (1);
-            }
-          
-          if (checksum != current_func->checksum)
-            die (".da file checksum doesn't match checksum from .bbg file\n");
-          
-          if (!string_get_int (contents, i, &val))
-            die ("no arc count for function\n");
-          
-          i += 4;
-          claimed_n_arcs = val;
-
-          /* For each arc in the profile, find the corresponding
-           * arc in the function and increment its count
-           */
-          n_arcs = 0;
-          while (n_arcs < claimed_n_arcs)
-            {
-              if (!string_get_int64 (contents, i, &v64))
-                die ("did not get execution count for arc\n");
-              
-              i += 8;
-
-              /* Find the next arc in the function that isn't on tree */
-              while (current_arc == NULL ||
-                     current_arc->on_tree)
-                {
-                  if (current_arc == NULL)
-                    {
-                      ++current_block;
-              
-                      if (current_block >= current_func->n_blocks)
-                        die ("too many blocks in function\n");
-              
-                      current_arc = current_func->block_graph[current_block].succ;
-                    }
-                  else
-                    {
-                      current_arc = current_arc->succ_next;
-                    }
-                }
-              
-              _dbus_assert (current_arc != NULL);
-              _dbus_assert (!current_arc->on_tree);
-              
-              current_arc->arc_count = v64;
-              current_arc->count_valid = TRUE;
-              current_func->block_graph[current_block].succ_count -= 1;
-              current_func->block_graph[current_arc->target].pred_count -= 1;
-              
-              ++n_arcs;
-              
-              current_arc = current_arc->succ_next;
-            }
-
-          _dbus_string_free (&funcname);
-
-          link = _dbus_list_get_next_link (functions, link);
-          ++n_functions;
-
-          if (link == NULL && n_functions < claimed_n_functions)
-            {
-              fprintf (stderr, "Ran out of functions loading .da file\n");
-              goto no_more_functions;
-            }
-        }
-
-    no_more_functions:
-      
-      ++n_sections;
-    }
-}
-#else /* DBUS_HAVE_GCC33_GCOV */
-static void
-add_counts_from_da (const DBusString  *contents,
-                    DBusList         **functions)
-{
-  int i;
-  dbus_int64_t val;
-  int n_arcs;
-  int claimed_n_arcs;
-  DBusList *link;
-  Function *current_func;  
-  int current_block;
-  Arc *current_arc;
-
-#if 0
-  printf ("Loading execution count for each arc from .da file\n");
-#endif
-  
-  i = 0;
-  if (!string_get_int64 (contents, i, &val))
-    return;
-
-  i += 8;
-  
-  claimed_n_arcs = val;
-
-  link = _dbus_list_get_first_link (functions);
-  if (link == NULL)
-    goto done;
-
-  current_func = link->data;
-  current_block = 0;
-  current_arc = current_func->block_graph[current_block].succ;
-  
-  n_arcs = 0;
-  while (string_get_int64 (contents, i, &val))
-    {
-      i += 8;
-
-      while (current_arc == NULL ||
-             current_arc->on_tree)
-        {
-          if (current_arc == NULL)
-            {
-              ++current_block;
-              
-              if (current_block == current_func->n_blocks)
-                {
-                  link = _dbus_list_get_next_link (functions, link);
-                  if (link == NULL)
-                    {
-                      fprintf (stderr, "Ran out of functions loading .da file\n");
-                      goto done;
-                    }
-                  current_func = link->data;
-                  current_block = 0;
-                }
-              
-              current_arc = current_func->block_graph[current_block].succ;
-            }
-          else
-            {
-              current_arc = current_arc->succ_next;
-            }
-        }
-
-      _dbus_assert (current_arc != NULL);
-      _dbus_assert (!current_arc->on_tree);
-
-      current_arc->arc_count = val;
-      current_arc->count_valid = TRUE;
-      current_func->block_graph[current_block].succ_count -= 1;
-      current_func->block_graph[current_arc->target].pred_count -= 1;
-      
-      ++n_arcs;
-
-      current_arc = current_arc->succ_next;
-    }
-
- done:
-  
-  if (n_arcs != claimed_n_arcs)
-    {
-      fprintf (stderr, "File claimed to have %d arcs but only had %d\n",
-               claimed_n_arcs, n_arcs);
-      exit (1);
-    }
-
-#if 0
-  printf ("%d arcs in file\n", n_arcs);
-#endif
-}
-#endif
-
-static void
-function_solve_graph (Function *func)
-{
-  int passes, changes;
-  dbus_int64_t total;
-  int i;
-  Arc *arc;
-  Block *block_graph;
-  int n_blocks;
-
-#if 0
-  printf ("Solving function graph\n");
-#endif
-  
-  n_blocks = func->n_blocks;
-  block_graph = func->block_graph;
-
-  /* For every block in the file,
-     - if every exit/entrance arc has a known count, then set the block count
-     - if the block count is known, and every exit/entrance arc but one has
-     a known execution count, then set the count of the remaining arc
-
-     As arc counts are set, decrement the succ/pred count, but don't delete
-     the arc, that way we can easily tell when all arcs are known, or only
-     one arc is unknown.  */
-
-  /* The order that the basic blocks are iterated through is important.
-     Since the code that finds spanning trees starts with block 0, low numbered
-     arcs are put on the spanning tree in preference to high numbered arcs.
-     Hence, most instrumented arcs are at the end.  Graph solving works much
-     faster if we propagate numbers from the end to the start.
-
-     This takes an average of slightly more than 3 passes.  */
-
-  changes = 1;
-  passes = 0;
-  while (changes)
-    {
-      passes++;
-      changes = 0;
-
-      for (i = n_blocks - 1; i >= 0; i--)
-       {
-         if (! block_graph[i].count_valid)
-           {
-             if (block_graph[i].succ_count == 0)
-               {
-                 total = 0;
-                 for (arc = block_graph[i].succ; arc;
-                      arc = arc->succ_next)
-                   total += arc->arc_count;
-                 block_graph[i].exec_count = total;
-                 block_graph[i].count_valid = 1;
-                 changes = 1;
-               }
-             else if (block_graph[i].pred_count == 0)
-               {
-                 total = 0;
-                 for (arc = block_graph[i].pred; arc;
-                      arc = arc->pred_next)
-                   total += arc->arc_count;
-                 block_graph[i].exec_count = total;
-                 block_graph[i].count_valid = 1;
-                 changes = 1;
-               }
-           }
-         if (block_graph[i].count_valid)
-           {
-             if (block_graph[i].succ_count == 1)
-               {
-                 total = 0;
-                 /* One of the counts will be invalid, but it is zero,
-                    so adding it in also doesn't hurt.  */
-                 for (arc = block_graph[i].succ; arc;
-                      arc = arc->succ_next)
-                   total += arc->arc_count;
-                 /* Calculate count for remaining arc by conservation.  */
-                 total = block_graph[i].exec_count - total;
-                 /* Search for the invalid arc, and set its count.  */
-                 for (arc = block_graph[i].succ; arc;
-                      arc = arc->succ_next)
-                   if (! arc->count_valid)
-                     break;
-                 if (! arc)
-                   die ("arc == NULL\n");
-                 arc->count_valid = 1;
-                 arc->arc_count = total;
-                 block_graph[i].succ_count -= 1;
-
-                 block_graph[arc->target].pred_count -= 1;
-                 changes = 1;
-               }
-             if (block_graph[i].pred_count == 1)
-               {
-                 total = 0;
-                 /* One of the counts will be invalid, but it is zero,
-                    so adding it in also doesn't hurt.  */
-                 for (arc = block_graph[i].pred; arc;
-                      arc = arc->pred_next)
-                   total += arc->arc_count;
-                 /* Calculate count for remaining arc by conservation.  */
-                 total = block_graph[i].exec_count - total;
-                 /* Search for the invalid arc, and set its count.  */
-                 for (arc = block_graph[i].pred; arc;
-                      arc = arc->pred_next)
-                   if (! arc->count_valid)
-                     break;
-                 if (! arc)
-                    die ("arc == NULL\n");
-                 arc->count_valid = 1;
-                 arc->arc_count = total;
-                 block_graph[i].pred_count -= 1;
-
-                 block_graph[arc->source].succ_count -= 1;
-                 changes = 1;
-               }
-           }
-       }
-    }
-
-  /* If the graph has been correctly solved, every block will have a
-   * succ and pred count of zero.
-   */
-  {
-    dbus_bool_t header = FALSE;
-    for (i = 0; i < n_blocks; i++)
-      {
-        if (block_graph[i].succ_count || block_graph[i].pred_count)
-          {
-            if (!header)
-              {
-                fprintf (stderr, "WARNING: Block graph solved incorrectly for function %s\n",
-                         func->name);
-                fprintf (stderr, " this error reflects a bug in decode-gcov.c or perhaps bogus data\n");
-                header = TRUE;
-              }
-            fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n",
-                     i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count);
-          }
-      }
-  }
-}
-
-static void
-solve_graphs (DBusList **functions)
-{
-  DBusList *link;
-
-  link = _dbus_list_get_first_link (functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      function_solve_graph (func);
-      
-      link = _dbus_list_get_next_link (functions, link);
-    }
-}
-
-static void
-load_functions_for_c_file (const DBusString *filename,
-                           DBusList        **functions)
-{
-  DBusString bbg_filename;
-  DBusString da_filename;
-  DBusString gcno_filename;
-  DBusString gcda_filename;
-  DBusString contents;
-  DBusString *name;
-  DBusError error;
-
-  /* With latest gcc it's .gcno instead of .bbg and
-   * gcda instead of .da
-   */
-  
-  dbus_error_init (&error);
-  
-  if (!_dbus_string_init (&bbg_filename) ||
-      !_dbus_string_init (&da_filename) ||
-      !_dbus_string_init (&gcno_filename) ||
-      !_dbus_string_init (&gcda_filename) ||
-      !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
-      !_dbus_string_copy (filename, 0, &da_filename, 0) ||
-      !_dbus_string_copy (filename, 0, &gcno_filename, 0) ||
-      !_dbus_string_copy (filename, 0, &gcda_filename, 0) ||
-      !_dbus_string_init (&contents))
-    die ("no memory\n");
-
-  _dbus_string_shorten (&bbg_filename, 2);
-  _dbus_string_shorten (&da_filename, 2);
-
-  if (!_dbus_string_append (&bbg_filename, ".bbg") ||
-      !_dbus_string_append (&da_filename, ".da") ||
-      !_dbus_string_append (&bbg_filename, ".gcno") ||
-      !_dbus_string_append (&bbg_filename, ".gcda"))
-    die ("no memory\n");
-
-  if (_dbus_file_exists (_dbus_string_get_const_data (&gcno_filename)))
-    name = &gcno_filename;
-  else
-    name = &bbg_filename;
-  
-  if (!_dbus_file_get_contents (&contents, name,
-                                &error))
-    {
-      fprintf (stderr, "Could not open file: %s\n",
-               error.message);
-      exit (1);
-    }
-
-  get_functions_from_bbg (&contents, functions);
-
-  _dbus_string_set_length (&contents, 0);
-
-  if (_dbus_file_exists (_dbus_string_get_const_data (&gcda_filename)))
-    name = &gcda_filename;
-  else
-    name = &da_filename;
-  
-  if (!_dbus_file_get_contents (&contents, name,
-                                &error))
-    {
-      /* Try .libs/file.da */
-      int slash;
-
-      if (_dbus_string_find_byte_backward (name,
-                                           _dbus_string_get_length (name),
-                                           '/',
-                                           &slash))
-        {
-          DBusString libs;
-          _dbus_string_init_const (&libs, "/.libs");
-
-          if (!_dbus_string_copy (&libs, 0, name, slash))
-            die ("no memory");
-
-          dbus_error_free (&error);
-          if (!_dbus_file_get_contents (&contents, name,
-                                        &error))
-            {
-              fprintf (stderr, "Could not open file: %s\n",
-                       error.message);
-              exit (1);
-            }
-        }
-      else
-        {
-          fprintf (stderr, "Could not open file: %s\n",
-                   error.message);
-          exit (1);
-        }
-    }
-  
-  add_counts_from_da (&contents, functions);
-  
-  solve_graphs (functions);
-
-  _dbus_string_free (&contents);
-  _dbus_string_free (&da_filename);
-  _dbus_string_free (&bbg_filename);
-}
-
-static void
-get_lines_from_bb_file (const DBusString *contents,
-                        File             *fl)
-{
-  int i;
-  long val;
-  int n_functions;
-  dbus_bool_t in_our_file;
-  DBusList *link;
-  Function *func;
-  int block;
-
-#if 0
-  printf ("Getting line numbers for blocks from .bb file\n");
-#endif
-  
-  /* There's this "filename" field in the .bb file which
-   * mysteriously comes *after* the first function in the
-   * file in the .bb file; and every .bb file seems to
-   * have only one filename. I don't understand
-   * what's going on here, so just set in_our_file = TRUE
-   * at the start categorically.
-   */
-  
-  block = 0;
-  func = NULL;
-  in_our_file = TRUE;
-  link = _dbus_list_get_first_link (&fl->functions);
-  n_functions = 0;
-  i = 0;
-  while (string_get_int (contents, i, &val))
-    {
-      i += 4;
-      
-      switch (val)
-        {
-        case BB_FILENAME:
-          {
-            DBusString f;
-
-            if (!_dbus_string_init (&f))
-              die ("no memory\n");
-
-            if (string_get_string (contents, i,
-                                   BB_FILENAME,
-                                   &f, &i))
-              {
-                /* fl->name is a full path and the filename in .bb is
-                 * not.
-                 */
-                DBusString tmp_str;
-
-                _dbus_string_init_const (&tmp_str, fl->name);
-                
-                if (_dbus_string_ends_with_c_str (&tmp_str,
-                                                  _dbus_string_get_const_data (&f)))
-                  in_our_file = TRUE;
-                else
-                  in_our_file = FALSE;
-                
-#if 0
-                fprintf (stderr,
-                         "File %s in .bb, looking for %s, in_our_file = %d\n",
-                         _dbus_string_get_const_data (&f),
-                         fl->name,
-                         in_our_file);
-#endif
-              }
-            _dbus_string_free (&f);
-          }
-          break;
-        case BB_FUNCTION:
-          {
-            DBusString f;
-            if (!_dbus_string_init (&f))
-              die ("no memory\n");
-
-            if (string_get_string (contents, i,
-                                   BB_FUNCTION,
-                                   &f, &i))
-              {
-#if 0
-                fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
-#endif
-
-                block = 0;
-                
-                if (in_our_file)
-                  {
-                    if (link == NULL)
-                      {
-                        fprintf (stderr, "No function object for function %s\n",
-                                 _dbus_string_get_const_data (&f));
-                      }
-                    else
-                      {
-                        func = link->data;
-                        link = _dbus_list_get_next_link (&fl->functions, link);
-
-                        if (func->name == NULL)
-                          {
-                            if (!_dbus_string_copy_data (&f, &func->name))
-                              die ("no memory\n");
-                          }
-                        else
-                          {
-                            if (!_dbus_string_equal_c_str (&f, func->name))
-                              {
-                                fprintf (stderr, "got function name \"%s\" (%d) from .bbg file, but \"%s\" (%d) from .bb file\n",
-                                         func->name, strlen (func->name),
-                                         _dbus_string_get_const_data (&f),
-                                         _dbus_string_get_length (&f));
-
-                              }
-                          }
-                      }
-                  }
-              }
-            _dbus_string_free (&f);
-
-            n_functions += 1;
-          }
-          break;
-        case BB_ENDOFLIST:
-          block += 1;
-          break;
-        default:
-#if 0
-          fprintf (stderr, "Line %ld\n", val);
-#endif
-
-          if (val >= fl->n_lines)
-            {
-              fprintf (stderr, "Line %ld but file only has %d lines\n",
-                       val, fl->n_lines);
-            }
-          else if (func != NULL)
-            {
-              val -= 1; /* To convert the 1-based line number to 0-based */
-              _dbus_assert (val >= 0);
-              
-              if (block < func->n_blocks)
-                {
-                  if (!_dbus_list_append (&func->block_graph[block].lines,
-                                          &fl->lines[val]))
-                    die ("no memory\n");
-                  
-                  
-                  if (!_dbus_list_append (&fl->lines[val].blocks,
-                                          &func->block_graph[block]))
-                    die ("no memory\n");
-                }
-              else
-                {
-                  fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
-                           block, func->n_blocks);
-                }
-            }
-          else
-            {
-              fprintf (stderr, "Line %ld given outside of any function\n",
-                       val);
-            }
-          
-          break;
-        }
-    }
-
-#if 0
-  printf ("%d functions in file\n", n_functions);
-#endif
-}
-
-
-static void
-load_block_line_associations (const DBusString *filename,
-                              File             *f)
-{
-  DBusString bb_filename;
-  DBusString contents;
-  DBusError error;
-
-  dbus_error_init (&error);
-  
-  if (!_dbus_string_init (&bb_filename) ||
-      !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
-      !_dbus_string_init (&contents))
-    die ("no memory\n");
-
-  _dbus_string_shorten (&bb_filename, 2);
-
-  if (!_dbus_string_append (&bb_filename, ".bb"))
-    die ("no memory\n");
-      
-  if (!_dbus_file_get_contents (&contents, &bb_filename,
-                                &error))
-    {
-      fprintf (stderr, "Could not open file: %s\n",
-               error.message);
-      exit (1);
-    }
-  
-  get_lines_from_bb_file (&contents, f);
-
-  _dbus_string_free (&contents);
-  _dbus_string_free (&bb_filename);
-}
-
-static int
-count_lines_in_string (const DBusString *str)
-{
-  int n_lines;
-  const char *p;
-  const char *prev;
-  const char *end;
-  const char *last_line_end;
-
-#if 0
-  printf ("Counting lines in source file\n");
-#endif
-  
-  n_lines = 0;  
-  prev = NULL;
-  p = _dbus_string_get_const_data (str);
-  end = p + _dbus_string_get_length (str);
-  last_line_end = p;
-  while (p != end)
-    {
-      /* too lazy to handle \r\n as one linebreak */
-      if (*p == '\n' || *p == '\r')
-        {
-          ++n_lines;
-          last_line_end = p + 1;
-        }
-
-      prev = p;
-      ++p;
-    }
-
-  if (last_line_end != p)
-    ++n_lines;
-  
-  return n_lines;
-}
-
-static void
-fill_line_content (const DBusString *str,
-                   Line             *lines)
-{
-  int n_lines;
-  const char *p;
-  const char *prev;
-  const char *end;
-  const char *last_line_end;
-
-#if 0
-  printf ("Saving contents of each line in source file\n");
-#endif
-  
-  n_lines = 0;
-  prev = NULL;
-  p = _dbus_string_get_const_data (str);
-  end = p + _dbus_string_get_length (str);
-  last_line_end = p;
-  while (p != end)
-    {
-      if (*p == '\n' || *p == '\r')
-        {
-          lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
-          if (lines[n_lines].text == NULL)
-            die ("no memory\n");
-
-          memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
-          lines[n_lines].number = n_lines + 1;
-          
-          ++n_lines;
-
-          last_line_end = p + 1;
-        }
-
-      prev = p;
-      ++p;
-    }
-
-  if (p != last_line_end)
-    {
-      memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
-      ++n_lines;
-    }
-}
-
-static void
-mark_inside_dbus_build_tests (File  *f)
-{
-  int i;
-  DBusList *link;
-  int inside_depth;
-
-  inside_depth = 0;
-  i = 0;
-  while (i < f->n_lines)
-    {
-      Line *l = &f->lines[i];
-      dbus_bool_t is_verbose;
-
-      is_verbose = strstr (l->text, "_dbus_verbose") != NULL;
-
-      if (inside_depth == 0)
-        {
-          const char *a, *b;
-          
-          a = strstr (l->text, "#if");
-          b = strstr (l->text, "DBUS_BUILD_TESTS");
-          if (a && b && (a < b))
-            inside_depth += 1;
-        }
-      else
-        {
-          if (strstr (l->text, "#if") != NULL)
-            inside_depth += 1;
-          else if (strstr (l->text, "#endif") != NULL)
-            inside_depth -= 1;
-        }
-
-      if (inside_depth > 0 || is_verbose)
-        {
-          /* Mark the line and its blocks */
-          DBusList *blink;
-
-          l->inside_dbus_build_tests = TRUE;
-          
-          blink = _dbus_list_get_first_link (&l->blocks);
-          while (blink != NULL)
-            {
-              Block *b = blink->data;
-
-              b->inside_dbus_build_tests = TRUE;
-              
-              blink = _dbus_list_get_next_link (&l->blocks, blink);
-            }
-        }
-      
-      ++i;
-    }
-
-  /* Now mark functions where for all blocks that are associated
-   * with a source line, the block is inside_dbus_build_tests.
-   */
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      /* The issue is that some blocks aren't associated with a source line.
-       * Assume they are inside/outside tests according to the source
-       * line of the preceding block. For the first block, make it
-       * match the first following block with a line associated.
-       */
-      if (func->block_graph[0].lines == NULL)
-        {
-          /* find first following line */
-          i = 1;
-          while (i < func->n_blocks)
-            {
-              if (func->block_graph[i].lines != NULL)
-                {
-                  func->block_graph[0].inside_dbus_build_tests =
-                    func->block_graph[i].inside_dbus_build_tests;
-                  break;
-                }
-              
-              ++i;
-            }
-        }
-
-      /* Now mark all blocks but the first */
-      i = 1;
-      while (i < func->n_blocks)
-        {
-          if (func->block_graph[i].lines == NULL)
-            {
-              func->block_graph[i].inside_dbus_build_tests =
-                func->block_graph[i-1].inside_dbus_build_tests;
-            }
-          
-          ++i;
-        }
-      
-      i = 0;
-      while (i < func->n_blocks)
-        {
-          /* Break as soon as any block is not a test block */
-          if (func->block_graph[i].lines != NULL &&
-              !func->block_graph[i].inside_dbus_build_tests)
-            break;
-          
-          ++i;
-        }
-
-      if (i == func->n_blocks)
-        func->inside_dbus_build_tests = TRUE;
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    } 
-}
-
-static void
-mark_coverage (File  *f)
-{
-  int i;
-  DBusList *link;
-  
-  i = 0;
-  while (i < f->n_lines)
-    {
-      Line *l = &f->lines[i];
-      DBusList *blink;
-      int n_blocks;
-      int n_blocks_executed;
-
-      n_blocks = 0;
-      n_blocks_executed = 0;
-      blink = _dbus_list_get_first_link (&l->blocks);
-      while (blink != NULL)
-        {
-          Block *b = blink->data;
-          
-          if (b->exec_count > 0)
-            n_blocks_executed += 1;
-
-          n_blocks += 1;
-          
-          blink = _dbus_list_get_next_link (&l->blocks, blink);
-        }
-
-      if (n_blocks_executed > 0 &&
-          n_blocks_executed < n_blocks)
-        l->partial = TRUE;
-
-      ++i;
-    }
-
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-      int i;
-      int n_test_blocks;
-      int n_test_blocks_executed;
-      int n_nontest_blocks;
-      int n_nontest_blocks_executed;
-      
-      n_test_blocks = 0;
-      n_test_blocks_executed = 0;
-      n_nontest_blocks = 0;
-      n_nontest_blocks_executed = 0;      
-
-      i = 0;
-      while (i < func->n_blocks)
-        {
-          if (!func->block_graph[i].inside_dbus_build_tests)
-            {
-              n_nontest_blocks += 1;
-
-              if (func->block_graph[i].exec_count > 0)
-                n_nontest_blocks_executed += 1;
-            }
-          else
-            {
-              n_test_blocks += 1;
-
-              if (func->block_graph[i].exec_count > 0)
-                n_test_blocks_executed += 1;
-            }
-
-          ++i;
-        }
-      
-      if (n_nontest_blocks_executed > 0 &&
-          n_nontest_blocks_executed < n_nontest_blocks)
-        func->partial = TRUE;
-
-      if (n_nontest_blocks_executed == 0 &&
-          n_nontest_blocks > 0)
-        func->unused = TRUE;
-      
-      func->n_test_blocks = n_test_blocks;
-      func->n_test_blocks_executed = n_test_blocks_executed;
-      func->n_nontest_blocks = n_nontest_blocks;
-      func->n_nontest_blocks_executed = n_nontest_blocks_executed;
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-}
-
-static File*
-load_c_file (const DBusString *filename)
-{
-  DBusString contents;
-  DBusError error;
-  File *f;
-  
-  f = dbus_new0 (File, 1);
-  if (f == NULL)
-    die ("no memory\n");
-
-  if (!_dbus_string_copy_data (filename, &f->name))
-    die ("no memory\n");
-  
-  if (!_dbus_string_init (&contents))
-    die ("no memory\n");
-      
-  dbus_error_init (&error);
-
-  if (!_dbus_file_get_contents (&contents, filename,
-                                &error))
-    {
-      fprintf (stderr, "Could not open file: %s\n",
-               error.message);
-      dbus_error_free (&error);
-      exit (1);
-    }
-      
-  load_functions_for_c_file (filename, &f->functions);
-
-  f->n_lines = count_lines_in_string (&contents);
-  f->lines = dbus_new0 (Line, f->n_lines);
-  if (f->lines == NULL)
-    die ("no memory\n");
-
-  fill_line_content (&contents, f->lines);
-  
-  _dbus_string_free (&contents);
-
-  load_block_line_associations (filename, f);
-
-  mark_inside_dbus_build_tests (f);
-  mark_coverage (f);
-  
-  return f;
-}
-
-typedef struct Stats Stats;
-
-struct Stats
-{
-  int n_blocks;
-  int n_blocks_executed;
-  int n_blocks_inside_dbus_build_tests;
-  
-  int n_lines; /* lines that have blocks on them */
-  int n_lines_executed;
-  int n_lines_partial;
-  int n_lines_inside_dbus_build_tests;
-  
-  int n_functions;
-  int n_functions_executed;
-  int n_functions_partial;
-  int n_functions_inside_dbus_build_tests;
-};
-
-static dbus_bool_t
-line_was_executed (Line *l)
-{
-  DBusList *link;
-
-  link = _dbus_list_get_first_link (&l->blocks);
-  while (link != NULL)
-    {
-      Block *b = link->data;
-
-      if (b->exec_count > 0)
-        return TRUE;
-      
-      link = _dbus_list_get_next_link (&l->blocks, link);
-    }
-
-  return FALSE;
-}
-
-
-static int
-line_exec_count (Line *l)
-{
-  DBusList *link;
-  dbus_int64_t total;
-
-  total = 0;
-  link = _dbus_list_get_first_link (&l->blocks);
-  while (link != NULL)
-    {
-      Block *b = link->data;
-
-      total += b->exec_count;
-      
-      link = _dbus_list_get_next_link (&l->blocks, link);
-    }
-
-  return total;
-}
-
-static void
-merge_stats_for_file (Stats *stats,
-                      File  *f)
-{
-  int i;
-  DBusList *link;
-  
-  for (i = 0; i < f->n_lines; ++i)
-    {
-      Line *l = &f->lines[i];
-      
-      if (l->inside_dbus_build_tests)
-        {
-          stats->n_lines_inside_dbus_build_tests += 1;
-          continue;
-        }
-      
-      if (line_was_executed (l))
-        stats->n_lines_executed += 1;
-
-      if (l->blocks != NULL)
-        stats->n_lines += 1;
-
-      if (l->partial)
-        stats->n_lines_partial += 1;
-    }
-
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (func->inside_dbus_build_tests)
-        stats->n_functions_inside_dbus_build_tests += 1;
-      else
-        {
-          stats->n_functions += 1;
-
-          if (!func->unused)
-            stats->n_functions_executed += 1;
-          
-          if (func->partial)
-            stats->n_functions_partial += 1;
-        }
-
-      stats->n_blocks_inside_dbus_build_tests +=
-        func->n_test_blocks;
-      
-      stats->n_blocks_executed +=
-        func->n_nontest_blocks_executed;
-      
-      stats->n_blocks +=
-        func->n_nontest_blocks;
-
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-}
-
-/* The output of this matches gcov exactly ("diff" shows no difference) */
-static void
-print_annotated_source_gcov_format (File *f)
-{
-  int i;
-  
-  i = 0;
-  while (i < f->n_lines)
-    {
-      Line *l = &f->lines[i];
-
-      if (l->blocks != NULL)
-        {
-          int exec_count;
-          
-          exec_count = line_exec_count (l);
-          
-          if (exec_count > 0)
-            printf ("%12d    %s\n",
-                    exec_count, l->text);
-          else
-            printf ("      ######    %s\n", l->text);
-        }
-      else
-        {
-          printf ("\t\t%s\n", l->text);
-        }
-          
-      ++i;
-    }
-}
-
-static void
-print_annotated_source (File *f)
-{
-  int i;
-  
-  i = 0;
-  while (i < f->n_lines)
-    {
-      Line *l = &f->lines[i];
-
-      if (l->inside_dbus_build_tests)
-        printf ("*");
-      else
-        printf (" ");
-      
-      if (l->blocks != NULL)
-        {
-          int exec_count;
-          
-          exec_count = line_exec_count (l);
-          
-          if (exec_count > 0)
-            printf ("%12d    %s\n",
-                    exec_count, l->text);
-          else
-            printf ("      ######    %s\n", l->text);
-        }
-      else
-        {
-          printf ("\t\t%s\n", l->text);
-        }
-          
-      ++i;
-    }
-}
-
-static void
-print_block_superdetails (File *f)
-{
-  DBusList *link;
-  int i;
-  
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      printf ("=== %s():\n", func->name);
-
-      i = 0;
-      while (i < func->n_blocks)
-        {
-          Block *b = &func->block_graph[i];
-          DBusList *l;
-          
-          printf ("  %5d executed %d times%s\n", i,
-                  (int) b->exec_count,
-                  b->inside_dbus_build_tests ?
-                  " [inside DBUS_BUILD_TESTS]" : "");
-                  
-          l = _dbus_list_get_first_link (&b->lines);
-          while (l != NULL)
-            {
-              Line *line = l->data;
-
-              printf ("4%d\t%s\n", line->number, line->text);
-
-              l = _dbus_list_get_next_link (&b->lines, l);
-            }
-          
-          ++i;
-        }
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-}
-
-static void
-print_one_file (const DBusString *filename)
-{
-  if (_dbus_string_ends_with_c_str (filename, ".bb"))
-    {
-      DBusString contents;
-      DBusError error;
-      
-      if (!_dbus_string_init (&contents))
-        die ("no memory\n");
-      
-      dbus_error_init (&error);
-
-      if (!_dbus_file_get_contents (&contents, filename,
-                                    &error))
-        {
-          fprintf (stderr, "Could not open file: %s\n",
-                   error.message);
-          dbus_error_free (&error);
-          exit (1);
-        }
-      
-      dump_bb_file (&contents);
-
-      _dbus_string_free (&contents);
-    }
-  else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
-    {
-      DBusString contents;
-      DBusError error;
-      
-      if (!_dbus_string_init (&contents))
-        die ("no memory\n");
-      
-      dbus_error_init (&error);
-
-      if (!_dbus_file_get_contents (&contents, filename,
-                                    &error))
-        {
-          fprintf (stderr, "Could not open file: %s\n",
-                   error.message);
-          dbus_error_free (&error);
-          exit (1);
-        }
-      
-      dump_bbg_file (&contents);
-
-      _dbus_string_free (&contents);
-    }
-  else if (_dbus_string_ends_with_c_str (filename, ".da"))
-    {
-      DBusString contents;
-      DBusError error;
-      
-      if (!_dbus_string_init (&contents))
-        die ("no memory\n");
-      
-      dbus_error_init (&error);
-
-      if (!_dbus_file_get_contents (&contents, filename,
-                                    &error))
-        {
-          fprintf (stderr, "Could not open file: %s\n",
-                   error.message);
-          dbus_error_free (&error);
-          exit (1);
-        }
-      
-      dump_da_file (&contents);
-
-      _dbus_string_free (&contents);
-    }
-  else if (_dbus_string_ends_with_c_str (filename, ".c"))
-    {
-      File *f;
-      
-      f = load_c_file (filename);
-
-      print_annotated_source (f);
-    }
-  else
-    {
-      fprintf (stderr, "Unknown file type %s\n",
-               _dbus_string_get_const_data (filename));
-      exit (1);
-    }
-}
-
-static void
-print_untested_functions (File *f)
-{
-  DBusList *link;
-  dbus_bool_t found;
-
-  found = FALSE;
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (func->unused &&
-          !func->inside_dbus_build_tests)
-        found = TRUE;
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-
-  if (!found)
-    return;
-  
-  printf ("Untested functions in %s\n", f->name);
-  printf ("=======\n");
-  
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (func->unused &&
-          !func->inside_dbus_build_tests)
-        printf ("  %s\n", func->name);
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-
-  printf ("\n");
-}
-
-static void
-print_poorly_tested_functions (File  *f,
-                               Stats *stats)
-{
-  DBusList *link;
-  dbus_bool_t found;
-
-#define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks)
-
-#define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks)
-  
-#define POORLY_TESTED(function) (!(function)->unused &&                 \
-                                 (function)->n_nontest_blocks > 0 &&    \
-                                 TEST_FRACTION (function) < AVERAGE_COVERAGE)
-  
-  found = FALSE;
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (POORLY_TESTED (func))
-        found = TRUE;
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-
-  if (!found)
-    return;
-
-  printf ("Below average functions in %s\n", f->name);
-  printf ("=======\n");
-  
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (POORLY_TESTED (func))
-        printf ("  %s (%d%%)\n", func->name,
-                (int) (TEST_FRACTION (func) * 100));
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-
-  printf ("\n");
-}
-
-static int
-func_cmp (const void *a,
-          const void *b)
-{
-  Function *af = *(Function**) a;
-  Function *bf = *(Function**) b;
-  int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed;
-  int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed;
-  
-  /* Sort by number of untested blocks */
-  return b_untested - a_untested;
-}
-
-static void
-print_n_untested_blocks_by_function (File  *f,
-                                     Stats *stats)
-{
-  DBusList *link;
-  Function **funcs;
-  int n_found;
-  int i;
-  
-  n_found = 0;
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (func->n_nontest_blocks_executed <
-          func->n_nontest_blocks)
-        n_found += 1;
-      
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-
-  if (n_found == 0)
-    return;
-
-  /* make an array so we can use qsort */
-  
-  funcs = dbus_new (Function*, n_found);
-  if (funcs == NULL)
-    return;
-  
-  i = 0;
-  link = _dbus_list_get_first_link (&f->functions);
-  while (link != NULL)
-    {
-      Function *func = link->data;
-
-      if (func->n_nontest_blocks_executed <
-          func->n_nontest_blocks)
-        {
-          funcs[i] = func;
-          ++i;
-        }
-
-      link = _dbus_list_get_next_link (&f->functions, link);
-    }
-
-  _dbus_assert (i == n_found);
-  
-  qsort (funcs, n_found, sizeof (Function*),
-         func_cmp);
-  
-  printf ("Incomplete functions in %s\n", f->name);
-  printf ("=======\n");
-
-  i = 0;
-  while (i < n_found)
-    {
-      Function *func = funcs[i];
-
-      printf ("  %s (%d/%d untested blocks)\n",
-              func->name,
-              func->n_nontest_blocks - func->n_nontest_blocks_executed,
-              func->n_nontest_blocks);
-      
-      ++i;
-    }
-
-  dbus_free (funcs);
-
-  printf ("\n");
-}
-
-static void
-print_stats (Stats      *stats,
-             const char *of_what)
-{
-  int completely;
-  
-  printf ("Summary (%s)\n", of_what);
-  printf ("=======\n");
-  printf ("  %g%% blocks executed (%d of %d)\n",
-          (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0,
-          stats->n_blocks_executed,
-          stats->n_blocks);
-
-  printf ("     (ignored %d blocks of test-only/debug-only code)\n",
-          stats->n_blocks_inside_dbus_build_tests);
-      
-  printf ("  %g%% functions executed (%d of %d)\n",
-          (stats->n_functions_executed / (double) stats->n_functions) * 100.0,
-          stats->n_functions_executed,
-          stats->n_functions);
-
-  completely = stats->n_functions_executed - stats->n_functions_partial;
-  printf ("  %g%% functions completely executed (%d of %d)\n",
-          (completely / (double) stats->n_functions) * 100.0,
-          completely,
-          stats->n_functions);
-
-  printf ("     (ignored %d functions of test-only/debug-only code)\n",
-          stats->n_functions_inside_dbus_build_tests);
-      
-  printf ("  %g%% lines executed (%d of %d)\n",
-          (stats->n_lines_executed / (double) stats->n_lines) * 100.0,
-          stats->n_lines_executed,
-          stats->n_lines);
-
-  completely = stats->n_lines_executed - stats->n_lines_partial;
-  printf ("  %g%% lines completely executed (%d of %d)\n",
-          (completely / (double) stats->n_lines) * 100.0,
-          completely,
-          stats->n_lines);
-
-  printf ("     (ignored %d lines of test-only/debug-only code)\n",
-          stats->n_lines_inside_dbus_build_tests);
-
-  printf ("\n");
-}
-
-typedef enum
-{
-  MODE_PRINT,
-  MODE_REPORT,
-  MODE_BLOCKS,
-  MODE_GCOV
-} Mode;
-
-int
-main (int argc, char **argv)
-{
-  DBusString filename;
-  int i;
-  Mode m;
-  
-  if (argc < 2)
-    {
-      fprintf (stderr, "Must specify files on command line\n");
-      return 1;
-    }
-
-  m = MODE_PRINT;
-  i = 1;
-
-  if (strcmp (argv[i], "--report") == 0)
-    {
-      m = MODE_REPORT;
-      ++i;
-    }
-  else if (strcmp (argv[i], "--blocks") == 0)
-    {
-      m = MODE_BLOCKS;
-      ++i;
-    }
-  else if (strcmp (argv[i], "--gcov") == 0)
-    {
-      m = MODE_GCOV;
-      ++i;
-    }
-
-  
-  if (i == argc)
-    {
-      fprintf (stderr, "Must specify files on command line\n");
-      return 1;
-    }
-
-  if (m == MODE_PRINT)
-    {
-      while (i < argc)
-        {
-          _dbus_string_init_const (&filename, argv[i]);
-          
-          print_one_file (&filename);
-          
-          ++i;
-        }
-    }
-  else if (m == MODE_BLOCKS || m == MODE_GCOV)
-    {
-      while (i < argc)
-        {
-          File *f;
-          
-          _dbus_string_init_const (&filename, argv[i]);
-      
-          f = load_c_file (&filename);
-
-          if (m == MODE_BLOCKS)
-            print_block_superdetails (f);
-          else if (m == MODE_GCOV)
-            print_annotated_source_gcov_format (f);
-          
-          ++i;
-        }
-    }
-  else if (m == MODE_REPORT)
-    {
-      Stats stats = { 0, };
-      DBusList *files;
-      DBusList *link;
-      DBusHashTable *stats_by_dir;
-      DBusHashIter iter;
-      
-      files = NULL;
-      while (i < argc)
-        {
-          _dbus_string_init_const (&filename, argv[i]);
-
-          if (_dbus_string_ends_with_c_str (&filename, ".c"))
-            {
-              File *f;
-              
-              f = load_c_file (&filename);
-              
-              if (!_dbus_list_append (&files, f))
-                die ("no memory\n");
-            }
-          else
-            {
-              fprintf (stderr, "Unknown file type %s\n",
-                       _dbus_string_get_const_data (&filename));
-              exit (1);
-            }
-          
-          ++i;
-        }
-
-      link = _dbus_list_get_first_link (&files);
-      while (link != NULL)
-        {
-          File *f = link->data;
-
-          merge_stats_for_file (&stats, f);
-          
-          link = _dbus_list_get_next_link (&files, link);
-        }
-
-      print_stats (&stats, "all files");
-
-      stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING,
-                                           dbus_free, dbus_free);
-      
-      link = _dbus_list_get_first_link (&files);
-      while (link != NULL)
-        {
-          File *f = link->data;
-          DBusString dirname;
-          char *dirname_c;
-          Stats *dir_stats;
-          
-          _dbus_string_init_const (&filename, f->name);
-            
-          if (!_dbus_string_init (&dirname))
-            die ("no memory\n");
-
-          if (!_dbus_string_get_dirname (&filename, &dirname) ||
-              !_dbus_string_copy_data (&dirname, &dirname_c))
-            die ("no memory\n");
-
-          dir_stats = _dbus_hash_table_lookup_string (stats_by_dir,
-                                                      dirname_c);
-
-          if (dir_stats == NULL)
-            {
-              dir_stats = dbus_new0 (Stats, 1);
-              if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c,
-                                                   dir_stats))
-                die ("no memory\n");
-            }
-          else
-            dbus_free (dirname_c);
-          
-          merge_stats_for_file (dir_stats, f);
-          
-          link = _dbus_list_get_next_link (&files, link);
-        }
-
-      _dbus_hash_iter_init (stats_by_dir, &iter);
-      while (_dbus_hash_iter_next (&iter))
-        {
-          const char *dirname = _dbus_hash_iter_get_string_key (&iter);
-          Stats *dir_stats = _dbus_hash_iter_get_value (&iter);
-
-          print_stats (dir_stats, dirname);
-        }
-
-      _dbus_hash_table_unref (stats_by_dir);
-
-      link = _dbus_list_get_first_link (&files);
-      while (link != NULL)
-        {
-          File *f = link->data;
-
-          print_untested_functions (f);
-          
-          link = _dbus_list_get_next_link (&files, link);
-        }
-
-      link = _dbus_list_get_first_link (&files);
-      while (link != NULL)
-        {
-          File *f = link->data;
-
-          print_poorly_tested_functions (f, &stats);
-          
-          link = _dbus_list_get_next_link (&files, link);
-        }
-
-      link = _dbus_list_get_first_link (&files);
-      while (link != NULL)
-        {
-          File *f = link->data;
-          
-          print_n_untested_blocks_by_function (f, &stats);
-          
-          link = _dbus_list_get_next_link (&files, link);
-        }
-    }
-  
-  return 0;
-}
diff --git a/tools/lcov.am b/tools/lcov.am
new file mode 100644 (file)
index 0000000..3178ba9
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright © 2008-2011 Collabora Ltd.
+# Copyright © 2008-2011 Nokia Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+lcov-reset:
+       lcov --directory @abs_top_srcdir@ --zerocounters
+
+# gcov takes ~forever to process config-parser.c for some reason?
+lcov-report:
+       true > bus/bus_test-config-parser.gcno
+       true > bus/dbus_daemon-config-parser.gcno
+       lcov --directory @abs_top_srcdir@ --capture \
+               --output-file @abs_top_builddir@/lcov.info
+       $(mkdir_p) @abs_top_builddir@/lcov.html
+       git_commit=`GIT_DIR=@abs_top_srcdir@/.git git log -1 --pretty=format:%h 2>/dev/null`;\
+       genhtml --title "@PACKAGE_STRING@ $$git_commit" \
+               --output-directory @abs_top_builddir@/lcov.html lcov.info
+       @echo
+       @echo 'lcov report can be found in:'
+       @echo 'file://@abs_top_builddir@/lcov.html/index.html'
+       @echo
+
+lcov-check:
+       $(MAKE) lcov-reset
+       $(MAKE) check $(LCOV_CHECK_ARGS)
+       $(MAKE) lcov-report
+
+## vim:set ft=automake: