diff options
| author | Havoc Pennington <hp@redhat.com> | 2003-04-05 10:04:04 +0000 | 
|---|---|---|
| committer | Havoc Pennington <hp@redhat.com> | 2003-04-05 10:04:04 +0000 | 
| commit | 3a96e4ffd99ec3f125e1891155ecb751b6e313a1 (patch) | |
| tree | f64f075a2100392883429e9aaa467be26a001669 | |
| parent | 03b9ca6d4ecf2577958530b8390d675c73a58825 (diff) | |
2003-04-05  Havoc Pennington  <hp@pobox.com>
	* Makefile.am (coverage-report.txt): add target "coverage-report.txt"
	* test/decode-gcov.c: hack up a little program to suck data
	out of gcov files. Yes this is sort of silly.
	* configure.in: define something in config.h and do an
	AM_CONDITIONAL when gcov is enabled
| -rw-r--r-- | ChangeLog | 10 | ||||
| -rw-r--r-- | Makefile.am | 30 | ||||
| -rw-r--r-- | bus/activation.c | 1 | ||||
| -rw-r--r-- | configure.in | 26 | ||||
| -rw-r--r-- | dbus/dbus-sysdeps.c | 4 | ||||
| -rw-r--r-- | test/Makefile.am | 12 | ||||
| -rw-r--r-- | test/decode-gcov.c | 1933 | 
7 files changed, 2012 insertions, 4 deletions
| @@ -1,3 +1,13 @@ +2003-04-05  Havoc Pennington  <hp@pobox.com> + +	* Makefile.am (coverage-report.txt): add target "coverage-report.txt" + +	* test/decode-gcov.c: hack up a little program to suck data  +	out of gcov files. Yes this is sort of silly. + +	* configure.in: define something in config.h and do an +	AM_CONDITIONAL when gcov is enabled +  2003-04-04  Havoc Pennington  <hp@redhat.com>  	* dbus/dbus-spawn.c, dbus/dbus-spawn.h: Change dbus_spawn to diff --git a/Makefile.am b/Makefile.am index 7ec5e7fc..25beb75e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,3 +24,33 @@ EXTRA_DIST =			\  all-local: Doxyfile +if DBUS_GCOV_ENABLED +clean-gcov: +	find -name "*.da" -o -name "*.gcov" | xargs rm || true + +## .PHONY so it always rebuilds it +.PHONY: coverage-report.txt +coverage-report.txt: +	GCOV_FILES=`find -name "*.bbg"` ;					\ +	C_FILES= ;								\ +	for F in $$GCOV_FILES; do						\ +		C=`echo $$F | sed -e 's/.bbg/.c/g'` ;				\ +		DA=`echo $$F | sed -e 's/.bbg/.da/g'` ;				\ +		if test -e $$DA ; 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
\ No newline at end of file diff --git a/bus/activation.c b/bus/activation.c index 9ae44225..2045baa2 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -28,6 +28,7 @@  #include <dbus/dbus-internals.h>  #include <dbus/dbus-hash.h>  #include <dbus/dbus-list.h> +#include <dbus/dbus-spawn.h>  #include <sys/types.h>  #include <dirent.h>  #include <errno.h> diff --git a/configure.in b/configure.in index 0238282e..3bc177b3 100644 --- a/configure.in +++ b/configure.in @@ -123,7 +123,13 @@ else    fi  fi -#### Assorted checks +if test x$enable_gcov = 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]) +fi +AM_CONDITIONAL(DBUS_GCOV_ENABLED, test x$enable_gcov = xyes) + +#### Integer sizes   AC_CHECK_SIZEOF(char)  AC_CHECK_SIZEOF(short) @@ -133,6 +139,24 @@ AC_CHECK_SIZEOF(void *)  AC_CHECK_SIZEOF(long long)  AC_CHECK_SIZEOF(__int64) +### this is only used in decode-gcov.c right now +case 8 in +$ac_cv_sizeof_int) +  dbusint64=int +  ;; +$ac_cv_sizeof_long) +  dbusint64=long +  ;; +$ac_cv_sizeof_long_long) +  dbusint64='long long' +  ;; +$ac_cv_sizeof___int64) +  dbusint64=__int64 +  ;; +esac + +AC_DEFINE_UNQUOTED(DBUS_INT64_TYPE, $dbusint64, [64-bit integer type]) +  ## byte order  AC_C_BIGENDIAN diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index 624af897..3588f762 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -2783,8 +2783,8 @@ _dbus_full_duplex_pipe (int        *fd1,      }    if (!blocking && -      !_dbus_set_fd_nonblocking (fds[0], NULL) || -      !_dbus_set_fd_nonblocking (fds[1], NULL)) +      (!_dbus_set_fd_nonblocking (fds[0], NULL) || +       !_dbus_set_fd_nonblocking (fds[1], NULL)))      {        dbus_set_error (error, _dbus_error_from_errno (errno),                        "Could not set full-duplex pipe nonblocking"); diff --git a/test/Makefile.am b/test/Makefile.am index c46b2797..ddbe69c4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -7,7 +7,13 @@ else  TEST_BINARIES=  endif -noinst_PROGRAMS= $(TEST_BINARIES) +if DBUS_GCOV_ENABLED +GCOV_BINARIES=decode-gcov +else +GCOV_BINARIES= +endif + +noinst_PROGRAMS= $(TEST_BINARIES) $(GCOV_BINARIES)  echo_client_SOURCES=				\  	echo-client.c				\ @@ -49,6 +55,9 @@ test_segfault_SOURCES =				\  test_sleep_forever_SOURCES =			\  	test-sleep-forever.c +decode_gcov_SOURCES=				\ +	decode-gcov.c +  TEST_LIBS=$(DBUS_TEST_LIBS) $(top_builddir)/dbus/libdbus-convenience.la  echo_client_LDADD=$(TEST_LIBS) @@ -58,6 +67,7 @@ unbase64_LDADD=$(TEST_LIBS)  break_loader_LDADD= $(TEST_LIBS)  #bus_test_LDADD=$(TEST_LIBS) $(top_builddir)/bus/libdbus-daemon.la  spawn_test_LDADD=$(TEST_LIBS) +decode_gcov_LDADD=$(TEST_LIBS)  EXTRA_DIST= diff --git a/test/decode-gcov.c b/test/decode-gcov.c new file mode 100644 index 00000000..0c30dd96 --- /dev/null +++ b/test/decode-gcov.c @@ -0,0 +1,1933 @@ + /* -*- mode: C; c-file-style: "gnu" -*- */ +/* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#define DBUS_COMPILATION /* cheat */ +#include <dbus/dbus-list.h> +#include <dbus/dbus-string.h> +#include <dbus/dbus-sysdeps.h> +#include <dbus/dbus-marshal.h> +#undef DBUS_COMPILATION +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +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; +} + +typedef DBUS_INT64_TYPE dbus_int64_t; + +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)) +    { +      i += 4; +       +      if (n == terminator) +        break; + +      _dbus_string_append_byte (val, n & 0xff); +      _dbus_string_append_byte (val, (n >> 8) & 0xff); +      _dbus_string_append_byte (val, (n >> 16) & 0xff); +      _dbus_string_append_byte (val, (n >> 24) & 0xff); +    } + +  *end = i; +   +  return TRUE; +} + +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 (string_get_int (contents, i, &val)) +    { +      long n_blocks_in_func; +      long n_arcs_in_func;  +      int j; +       +      n_blocks_in_func = val; +       +      i += 4; + +      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); +} + +/* 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); +    } +} + +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; +  Block *block_graph; +  int n_blocks; +  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 (string_get_int (contents, i, &val)) +    { +      Function *func; +      long n_blocks_in_func; +      long n_arcs_in_func;  +      int j; +       +      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"); + +      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\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)); +} + +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 +} + +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. +   */ +  for (i = 0; i < n_blocks; i++) +    { +      if (block_graph[i].succ_count || block_graph[i].pred_count) +        { +          fprintf (stderr, "Block graph solved incorrectly\n"); +          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); +          exit (1); +        } +    } +} + +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 contents; +  DBusError error; + +  dbus_error_init (&error); +   +  if (!_dbus_string_init (&bbg_filename) || +      !_dbus_string_init (&da_filename) || +      !_dbus_string_copy (filename, 0, &bbg_filename, 0) || +      !_dbus_string_copy (filename, 0, &da_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")) +    die ("no memory\n"); +       +  if (!_dbus_file_get_contents (&contents, &bbg_filename, +                                &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_get_contents (&contents, &da_filename, +                                &error)) +    { +      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 +                          { +                            die ("got two names for function?\n"); +                          } +                      } +                  } +              } +            _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_unused_functions (File  *f) +{ +  int i; +  DBusList *link; + +  link = _dbus_list_get_first_link (&f->functions); +  while (link != NULL) +    { +      Function *func = link->data; +      dbus_bool_t used; + +      used = FALSE; +      i = 0; +      while (i < func->n_blocks) +        { +          if (func->block_graph[i].exec_count > 0) +            used = TRUE; +           +          ++i; +        } + +      if (!used) +        func->unused = TRUE; + +      link = _dbus_list_get_next_link (&f->functions, link); +    } +} + +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]; + +      if (inside_depth == 0) +        { +          const char *a, *b; +           +          a = strstr (l->text, "#ifdef"); +          b = strstr (l->text, "DBUS_BUILD_TESTS"); +          if (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) +        { +          /* 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; +       +      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_partials (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_blocks; +      int n_blocks_executed; + +      n_blocks = 0; +      n_blocks_executed = 0; +      i = 0; +      while (i < func->n_blocks) +        { +          /* Break as soon as any block is not a test block */ +          if (func->block_graph[i].exec_count > 0) +            n_blocks_executed += 1; + +          n_blocks += 1; +          ++i; +        } + +      if (n_blocks_executed > 0 && +          n_blocks_executed < n_blocks) +        func->partial = TRUE; +       +      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_unused_functions (f); +  mark_inside_dbus_build_tests (f); +  mark_partials (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; +        } +           +      i = 0; +      while (i < func->n_blocks) +        { +          Block *b = &func->block_graph[i]; + +          if (b->inside_dbus_build_tests) +            stats->n_blocks_inside_dbus_build_tests += 1; +          else +            { +              if (b->exec_count > 0) +                stats->n_blocks_executed += 1; +               +              stats->n_blocks += 1; +            } +           +          ++i; +        } + +      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"); +} + +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; +      int completely; +       +      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); +        } + +      printf ("Summary\n"); +      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 inside DBUS_BUILD_TESTS)\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 inside DBUS_BUILD_TESTS)\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 inside DBUS_BUILD_TESTS)\n", +              stats.n_lines_inside_dbus_build_tests); + +      printf ("\n"); +       +      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); +        }       +    } +   +  return 0; +} | 
