OSDN Git Service

2012-02-02 Vladimir Makarov <vmakarov@redhat.com>
[pf3gnuchains/gcc-fork.git] / gcc / gcov.c
index dbafe81..9707111 100644 (file)
@@ -1,13 +1,15 @@
 /* Gcov.c: prepend line execution counts and branch probabilities to a
    source file.
-   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+   Free Software Foundation, Inc.
    Contributed by James E. Wilson of Cygnus Support.
    Mangled by Bob Manson of Cygnus Support.
+   Mangled further by Nathan Sidwell <nathan@codesourcery.com>
 
 Gcov 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, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 Gcov is distributed in the hope that it will be useful,
@@ -16,17 +18,8 @@ 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 Gcov; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
-
-/* ??? The code in final.c that produces the struct bb assumes that there is
-   no padding between the fields.  This is not necessary true.  The current
-   code can only be trusted if longs and pointers are the same size.  */
-
-/* ??? No need to print an execution count on every line, could just print
-   it on the first line of each block, and only print it on a subsequent
-   line in the same block if the count changes.  */
+along with Gcov; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 /* ??? Print a list of the ten blocks with the highest execution counts,
    and list the line numbers corresponding to those blocks.  Also, perhaps
@@ -36,1410 +29,2320 @@ Boston, MA 02111-1307, USA.  */
 /* ??? Should have an option to print the number of basic blocks, and the
    percent of them that are covered.  */
 
-/* ??? Does not correctly handle the case where two .bb files refer to the
-   same included source file.  For example, if one has a short file containing
-   only inline functions, which is then included in two other files, then
-   there will be two .bb files which refer to the include file, but there
-   is no way to get the total execution counts for the included file, can
-   only get execution counts for one or the other of the including files.  */
+/* Need an option to show individual block counts, and show
+   probabilities of fall through arcs.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "intl.h"
-#undef abort
+#include "diagnostic.h"
+#include "version.h"
 
-typedef HOST_WIDEST_INT gcov_type;
+#include <getopt.h>
+
+#define IN_GCOV 1
 #include "gcov-io.h"
+#include "gcov-io.c"
 
-/* The .bb file format consists of several lists of 4-byte integers
-   which are the line numbers of each basic block in the file.  Each
-   list is terminated by a zero.  These lists correspond to the basic
-   blocks in the reconstructed program flow graph.
+/* The gcno file is generated by -ftest-coverage option. The gcda file is
+   generated by a program compiled with -fprofile-arcs. Their formats
+   are documented in gcov-io.h.  */
 
-   A line number of -1 indicates that a source file name (padded to a
-   long boundary) follows.  The padded file name is followed by
-   another -1 to make it easy to scan past file names.  A -2 indicates
-   that a function name (padded to a long boundary) follows; the name
-   is followed by another -2 to make it easy to scan past the function
-   name.
+/* The functions in this file for creating and solution program flow graphs
+   are very similar to functions in the gcc source file profile.c.  In
+   some places we make use of the knowledge of how profile.c works to
+   select particular algorithms here.  */
 
-   The .bbg file contains enough info to enable gcov to reconstruct the
-   program flow graph.  The first word is the number of basic blocks,
-   the second word is the number of arcs, followed by the list of arcs
-   (source bb, dest bb pairs), then a -1, then the number of instrumented
-   arcs followed by the instrumented arcs, followed by another -1.  This
-   is repeated for each function.
+/* The code validates that the profile information read in corresponds
+   to the code currently being compiled.  Rather than checking for
+   identical files, the code below computes a checksum on the CFG
+   (based on the order of basic blocks and the arcs in the CFG).  If
+   the CFG checksum in the gcda file match the CFG checksum for the
+   code currently being compiled, the profile data will be used.  */
 
-   The .da file contains the execution count for each instrumented branch.
+/* This is the size of the buffer used to read in source file lines.  */
 
-   The .bb and .bbg files are created by giving GCC the -ftest-coverage option,
-   and the .da files are created when an executable compiled with
-   -fprofile-arcs is run.  */
+struct function_info;
+struct block_info;
+struct source_info;
 
-/* The functions in this file for creating and solution program flow graphs
-   are very similar to functions in the gcc source file profile.c.  */
+/* Describes an arc between two basic blocks.  */
 
-static const char gcov_version_string[] = "GNU gcov version 1.5\n";
+typedef struct arc_info
+{
+  /* source and destination blocks.  */
+  struct block_info *src;
+  struct block_info *dst;
 
-/* This is the size of the buffer used to read in source file lines.  */
+  /* transition counts.  */
+  gcov_type count;
+  /* used in cycle search, so that we do not clobber original counts.  */
+  gcov_type cs_count;
+
+  unsigned int count_valid : 1;
+  unsigned int on_tree : 1;
+  unsigned int fake : 1;
+  unsigned int fall_through : 1;
+
+  /* Arc to a catch handler.  */
+  unsigned int is_throw : 1;
+
+  /* Arc is for a function that abnormally returns.  */
+  unsigned int is_call_non_return : 1;
+
+  /* Arc is for catch/setjmp.  */
+  unsigned int is_nonlocal_return : 1;
 
-#define STRING_SIZE 200
+  /* Is an unconditional branch.  */
+  unsigned int is_unconditional : 1;
 
-/* One copy of this structure is created for each source file mentioned in the
-   .bb file.  */
+  /* Loop making arc.  */
+  unsigned int cycle : 1;
 
-struct sourcefile
+  /* Next branch on line.  */
+  struct arc_info *line_next;
+
+  /* Links to next arc on src and dst lists.  */
+  struct arc_info *succ_next;
+  struct arc_info *pred_next;
+} arc_t;
+
+/* Describes a basic block. Contains lists of arcs to successor and
+   predecessor blocks.  */
+
+typedef struct block_info
+{
+  /* Chain of exit and entry arcs.  */
+  arc_t *succ;
+  arc_t *pred;
+
+  /* Number of unprocessed exit and entry arcs.  */
+  gcov_type num_succ;
+  gcov_type num_pred;
+
+  /* Block execution count.  */
+  gcov_type count;
+  unsigned flags : 12;
+  unsigned count_valid : 1;
+  unsigned valid_chain : 1;
+  unsigned invalid_chain : 1;
+  unsigned exceptional : 1;
+
+  /* Block is a call instrumenting site.  */
+  unsigned is_call_site : 1; /* Does the call.  */
+  unsigned is_call_return : 1; /* Is the return.  */
+
+  /* Block is a landing pad for longjmp or throw.  */
+  unsigned is_nonlocal_return : 1;
+
+  union
+  {
+    struct
+    {
+     /* Array of line numbers and source files. source files are
+        introduced by a linenumber of zero, the next 'line number' is
+        the number of the source file.  Always starts with a source
+        file.  */
+      unsigned *encoding;
+      unsigned num;
+    } line; /* Valid until blocks are linked onto lines */
+    struct
+    {
+      /* Single line graph cycle workspace.  Used for all-blocks
+        mode.  */
+      arc_t *arc;
+      unsigned ident;
+    } cycle; /* Used in all-blocks mode, after blocks are linked onto
+              lines.  */
+  } u;
+
+  /* Temporary chain for solving graph, and for chaining blocks on one
+     line.  */
+  struct block_info *chain;
+
+} block_t;
+
+/* Describes a single function. Contains an array of basic blocks.  */
+
+typedef struct function_info
 {
+  /* Name of function.  */
   char *name;
-  int maxlineno;
-  struct sourcefile *next;
-};
+  unsigned ident;
+  unsigned lineno_checksum;
+  unsigned cfg_checksum;
 
-/* This points to the head of the sourcefile structure list.  */
+  /* The graph contains at least one fake incoming edge.  */
+  unsigned has_catch : 1;
 
-struct sourcefile *sources;
+  /* Array of basic blocks.  */
+  block_t *blocks;
+  unsigned num_blocks;
+  unsigned blocks_executed;
 
-/* One of these is dynamically created whenever we identify an arc in the
-   function.  */
+  /* Raw arc coverage counts.  */
+  gcov_type *counts;
+  unsigned num_counts;
 
-struct adj_list {
-  int source;
-  int target;
-  gcov_type arc_count;
-  unsigned int count_valid : 1;
-  unsigned int on_tree : 1;
-  unsigned int fake : 1;
-  unsigned int fall_through : 1;
-#if 0
-  /* Not needed for gcov, but defined in profile.c.  */
-  rtx branch_insn;
-#endif
-  struct adj_list *pred_next;
-  struct adj_list *succ_next;
-};
+  /* First line number & file.  */
+  unsigned line;
+  unsigned src;
 
-/* Count the number of basic blocks, and create an array of these structures,
-   one for each bb in the function.  */
+  /* Next function in same source file.  */
+  struct function_info *line_next;
 
-struct bb_info {
-  struct adj_list *succ;
-  struct adj_list *pred;
-  gcov_type succ_count;
-  gcov_type pred_count;
-  gcov_type exec_count;
-  unsigned int count_valid : 1;
-  unsigned int on_tree : 1;
-#if 0
-  /* Not needed for gcov, but defined in profile.c.  */
-  rtx first_insn;
-#endif
-};
+  /* Next function.  */
+  struct function_info *next;
+} function_t;
 
-/* When outputting branch probabilities, one of these structures is created
-   for each branch/call.  */
+/* Describes coverage of a file or function.  */
 
-struct arcdata
+typedef struct coverage_info
 {
-  gcov_type hits;
-  gcov_type total;
-  int call_insn;
-  struct arcdata *next;
-};
+  int lines;
+  int lines_executed;
 
-/* Used to save the list of bb_graphs, one per function.  */
+  int branches;
+  int branches_executed;
+  int branches_taken;
 
-struct bb_info_list {
-  /* Indexed by block number, holds the basic block graph for one function.  */
-  struct bb_info *bb_graph;
-  int num_blocks;
-  struct bb_info_list *next;
-};
+  int calls;
+  int calls_executed;
+
+  char *name;
+} coverage_t;
+
+/* Describes a single line of source. Contains a chain of basic blocks
+   with code on it.  */
+
+typedef struct line_info
+{
+  gcov_type count;        /* execution count */
+  union
+  {
+    arc_t *branches;      /* branches from blocks that end on this
+                             line. Used for branch-counts when not
+                             all-blocks mode.  */
+    block_t *blocks;       /* blocks which start on this line.  Used
+                             in all-blocks mode.  */
+  } u;
+  unsigned exists : 1;
+  unsigned unexceptional : 1;
+} line_t;
+
+/* Describes a file mentioned in the block graph.  Contains an array
+   of line info.  */
+
+typedef struct source_info
+{
+  /* Canonical name of source file.  */
+  char *name;
+  time_t file_time;
+
+  /* Array of line information.  */
+  line_t *lines;
+  unsigned num_lines;
+
+  coverage_t coverage;
+
+  /* Functions in this source file.  These are in ascending line
+     number order.  */
+  function_t *functions;
+} source_t;
+
+typedef struct name_map
+{
+  char *name;  /* Source file name */
+  unsigned src;  /* Source file */
+} name_map_t;
 
 /* Holds a list of function basic block graphs.  */
 
-static struct bb_info_list *bb_graph_list = 0;
+static function_t *functions;
+static function_t **fn_end = &functions;
+
+static source_t *sources;   /* Array of source files  */
+static unsigned n_sources;  /* Number of sources */
+static unsigned a_sources;  /* Allocated sources */
+
+static name_map_t *names;   /* Mapping of file names to sources */
+static unsigned n_names;    /* Number of names */
+static unsigned a_names;    /* Allocated names */
+
+/* This holds data summary information.  */
+
+static unsigned object_runs;
+static unsigned program_count;
+
+static unsigned total_lines;
+static unsigned total_executed;
+
+/* Modification time of graph file.  */
+
+static time_t bbg_file_time;
 
 /* Name and file pointer of the input file for the basic block graph.  */
 
 static char *bbg_file_name;
-static FILE *bbg_file;
+
+/* Stamp of the bbg file */
+static unsigned bbg_stamp;
 
 /* Name and file pointer of the input file for the arc count data.  */
 
 static char *da_file_name;
-static FILE *da_file;
-
-/* Name and file pointer of the input file for the basic block line counts.  */
 
-static char *bb_file_name;
-static FILE *bb_file;
+/* Data file is missing.  */
 
-/* Holds the entire contents of the bb_file read into memory.  */
+static int no_data_file;
 
-static char *bb_data;
+/* If there is several input files, compute and display results after
+   reading all data files.  This way if two or more gcda file refer to
+   the same source file (eg inline subprograms in a .h file), the
+   counts are added.  */
 
-/* Size of bb_data array in longs.  */
+static int multiple_files = 0;
 
-static long bb_data_size;
+/* Output branch probabilities.  */
 
-/* Name and file pointer of the output file.  */
+static int flag_branches = 0;
 
-static char *gcov_file_name;
-static FILE *gcov_file;
+/* Show unconditional branches too.  */
+static int flag_unconditional = 0;
 
-/* Name of the file mentioned on the command line.  */
+/* Output a gcov file if this is true.  This is on by default, and can
+   be turned off by the -n option.  */
 
-static char *input_file_name = 0;
+static int flag_gcov_file = 1;
 
-/* Output branch probabilities if true.  */
+/* Output progress indication if this is true.  This is off by default
+   and can be turned on by the -d option.  */
 
-static int output_branch_probs = 0;
+static int flag_display_progress = 0;
 
-/* Output a gcov file if this is true.  This is on by default, and can
-   be turned off by the -n option.  */
+/* For included files, make the gcov output file name include the name
+   of the input source file.  For example, if x.h is included in a.c,
+   then the output file name is a.c##x.h.gcov instead of x.h.gcov.  */
 
-static int output_gcov_file = 1;
+static int flag_long_names = 0;
 
-/* For included files, make the gcov output file name include the name of
-   the input source file.  For example, if x.h is included in a.c, then the
-   output file name is a.c.x.h.gcov instead of x.h.gcov.  This works only
-   when a single source file is specified.  */
+/* Output count information for every basic block, not merely those
+   that contain line number information.  */
 
-static int output_long_names = 0;
+static int flag_all_blocks = 0;
 
 /* Output summary info for each function.  */
 
-static int output_function_summary = 0;
+static int flag_function_summary = 0;
 
-/* Object directory file prefix.  This is the directory where .bb and .bbg
-   files are looked for, if non-zero.  */
+/* Object directory file prefix.  This is the directory/file where the
+   graph and data files are looked for, if nonzero.  */
 
 static char *object_directory = 0;
 
+/* Source directory prefix.  This is removed from source pathnames
+   that match, when generating the output file name.  */
+
+static char *source_prefix = 0;
+static size_t source_length = 0;
+
+/* Only show data for sources with relative pathnames.  Absolute ones
+   usually indicate a system header file, which although it may
+   contain inline functions, is usually uninteresting.  */
+static int flag_relative_only = 0;
+
+/* Preserve all pathname components. Needed when object files and
+   source files are in subdirectories. '/' is mangled as '#', '.' is
+   elided and '..' mangled to '^'.  */
+
+static int flag_preserve_paths = 0;
+
 /* Output the number of times a branch was taken as opposed to the percentage
-   of times it was taken.  Turned on by the -c option */
+   of times it was taken.  */
 
-static int output_branch_counts = 0;
+static int flag_counts = 0;
 
 /* Forward declarations.  */
-static void process_args PARAMS ((int, char **));
-static void open_files PARAMS ((void));
-static void read_files PARAMS ((void));
-static void scan_for_source_files PARAMS ((void));
-static void output_data PARAMS ((void));
-static void print_usage PARAMS ((void)) ATTRIBUTE_NORETURN;
-static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
-static struct adj_list *reverse_arcs PARAMS ((struct adj_list *));
-static void create_program_flow_graph PARAMS ((struct bb_info_list *));
-static void solve_program_flow_graph PARAMS ((struct bb_info_list *));
-static void calculate_branch_probs PARAMS ((struct bb_info_list *, int,
-                                           struct arcdata **, int));
-static void function_summary PARAMS ((void));
-
-extern int main PARAMS ((int, char **));
+static int process_args (int, char **);
+static void print_usage (int) ATTRIBUTE_NORETURN;
+static void print_version (void) ATTRIBUTE_NORETURN;
+static void process_file (const char *);
+static void generate_results (const char *);
+static void create_file_names (const char *);
+static int name_search (const void *, const void *);
+static int name_sort (const void *, const void *);
+static char *canonicalize_name (const char *);
+static unsigned find_source (const char *);
+static function_t *read_graph_file (void);
+static int read_count_file (function_t *);
+static void solve_flow_graph (function_t *);
+static void find_exception_blocks (function_t *);
+static void add_branch_counts (coverage_t *, const arc_t *);
+static void add_line_counts (coverage_t *, function_t *);
+static void executed_summary (unsigned, unsigned);
+static void function_summary (const coverage_t *, const char *);
+static const char *format_gcov (gcov_type, gcov_type, int);
+static void accumulate_line_counts (source_t *);
+static int output_branch_count (FILE *, int, const arc_t *);
+static void output_lines (FILE *, const source_t *);
+static char *make_gcov_file_name (const char *, const char *);
+static char *mangle_name (const char *, char *);
+static void release_structures (void);
+static void release_function (function_t *);
+extern int main (int, char **);
 
 int
-main (argc, argv)
-     int argc;
-     char **argv;
+main (int argc, char **argv)
 {
-/* LC_CTYPE determines the character set used by the terminal so it has be set
-   to output messages correctly.  */
+  int argno;
+  int first_arg;
+  const char *p;
 
-#ifdef HAVE_LC_MESSAGES
-  setlocale (LC_CTYPE, "");
-  setlocale (LC_MESSAGES, "");
-#else
-  setlocale (LC_ALL, "");
-#endif
+  p = argv[0] + strlen (argv[0]);
+  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+    --p;
+  progname = p;
+
+  xmalloc_set_program_name (progname);
 
-  (void) bindtextdomain (PACKAGE, localedir);
-  (void) textdomain (PACKAGE);
+  /* Unlock the stdio streams.  */
+  unlock_std_streams ();
 
-  process_args (argc, argv);
+  gcc_init_libintl ();
 
-  open_files ();
+  diagnostic_initialize (global_dc, 0);
 
-  read_files ();
+  /* Handle response files.  */
+  expandargv (&argc, &argv);
 
-  scan_for_source_files ();
+  a_names = 10;
+  names = XNEWVEC (name_map_t, a_names);
+  a_sources = 10;
+  sources = XNEWVEC (source_t, a_sources);
+  
+  argno = process_args (argc, argv);
+  if (optind == argc)
+    print_usage (true);
 
-  output_data ();
+  if (argc - argno > 1)
+    multiple_files = 1;
+
+  first_arg = argno;
+  
+  for (; argno != argc; argno++)
+    {
+      if (flag_display_progress)
+        printf("Processing file %d out of %d\n",  
+               argno - first_arg + 1, argc - first_arg);
+      process_file (argv[argno]);
+    }
+
+  generate_results (multiple_files ? NULL : argv[argc - 1]);
+
+  release_structures ();
 
   return 0;
 }
+\f
+/* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
+   otherwise the output of --help.  */
 
-static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2;
 static void
-fnotice VPARAMS ((FILE *file, const char *msgid, ...))
+print_usage (int error_p)
 {
-  VA_OPEN (ap, msgid);
-  VA_FIXEDARG (ap, FILE *, file);
-  VA_FIXEDARG (ap, const char *, msgid);
-
-  vfprintf (file, _(msgid), ap);
-  VA_CLOSE (ap);
+  FILE *file = error_p ? stderr : stdout;
+  int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
+
+  fnotice (file, "Usage: gcov [OPTION]... SOURCE|OBJ...\n\n");
+  fnotice (file, "Print code coverage information.\n\n");
+  fnotice (file, "  -h, --help                      Print this help, then exit\n");
+  fnotice (file, "  -v, --version                   Print version number, then exit\n");
+  fnotice (file, "  -a, --all-blocks                Show information for every basic block\n");
+  fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
+  fnotice (file, "  -c, --branch-counts             Given counts of branches taken\n\
+                                    rather than percentages\n");
+  fnotice (file, "  -n, --no-output                 Do not create an output file\n");
+  fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
+                                    source files\n");
+  fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
+  fnotice (file, "  -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
+  fnotice (file, "  -s, --source-prefix DIR         Source prefix to elide\n");
+  fnotice (file, "  -r, --relative-only             Only show data for relative sources\n");
+  fnotice (file, "  -p, --preserve-paths            Preserve all pathname components\n");
+  fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
+  fnotice (file, "  -d, --display-progress          Display progress information\n");
+  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
+          bug_report_url);
+  exit (status);
 }
 
-/* More 'friendly' abort that prints the line and file.
-   config.h can #define abort fancy_abort if you like that sort of thing.  */
-extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN;
+/* Print version information and exit.  */
 
-void
-fancy_abort ()
+static void
+print_version (void)
 {
-  fnotice (stderr, "Internal gcov abort.\n");
-  exit (FATAL_EXIT_CODE);
+  fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2012 Free Software Foundation, Inc.\n",
+          _("(C)"));
+  fnotice (stdout,
+          _("This is free software; see the source for copying conditions.\n"
+            "There is NO warranty; not even for MERCHANTABILITY or \n"
+            "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
+  exit (SUCCESS_EXIT_CODE);
 }
-\f
-/* Print a usage message and exit.  */
 
-static void
-print_usage ()
+static const struct option options[] =
 {
-  fnotice (stderr, "gcov [-b] [-v] [-n] [-l] [-f] [-o OBJDIR] file\n");
-  exit (FATAL_EXIT_CODE);
-}
+  { "help",                 no_argument,       NULL, 'h' },
+  { "version",              no_argument,       NULL, 'v' },
+  { "all-blocks",           no_argument,       NULL, 'a' },
+  { "branch-probabilities", no_argument,       NULL, 'b' },
+  { "branch-counts",        no_argument,       NULL, 'c' },
+  { "no-output",            no_argument,       NULL, 'n' },
+  { "long-file-names",      no_argument,       NULL, 'l' },
+  { "function-summaries",   no_argument,       NULL, 'f' },
+  { "preserve-paths",       no_argument,       NULL, 'p' },
+  { "relative-only",        no_argument,       NULL, 'r' },
+  { "object-directory",     required_argument, NULL, 'o' },
+  { "object-file",          required_argument, NULL, 'o' },
+  { "source-prefix",        required_argument, NULL, 's' },
+  { "unconditional-branches", no_argument,     NULL, 'u' },
+  { "display-progress",     no_argument,       NULL, 'd' },
+  { 0, 0, 0, 0 }
+};
 
-/* Parse the command line.  */
+/* Process args, return index to first non-arg.  */
 
-static void
-process_args (argc, argv)
-     int argc;
-     char **argv;
+static int
+process_args (int argc, char **argv)
 {
-  int i;
+  int opt;
 
-  for (i = 1; i < argc; i++)
+  while ((opt = getopt_long (argc, argv, "abcdfhlno:s:pruv", options, NULL)) != -1)
     {
-      if (argv[i][0] == '-')
+      switch (opt)
        {
-         if (argv[i][1] == 'b')
-           output_branch_probs = 1;
-         else if (argv[i][1] == 'c')
-           output_branch_counts = 1;
-         else if (argv[i][1] == 'v')
-           fputs (gcov_version_string, stderr);
-         else if (argv[i][1] == 'n')
-           output_gcov_file = 0;
-         else if (argv[i][1] == 'l')
-           output_long_names = 1;
-         else if (argv[i][1] == 'f')
-           output_function_summary = 1;
-         else if (argv[i][1] == 'o' && argv[i][2] == '\0')
-           object_directory = argv[++i];
-         else
-           print_usage ();
+       case 'a':
+         flag_all_blocks = 1;
+         break;
+       case 'b':
+         flag_branches = 1;
+         break;
+       case 'c':
+         flag_counts = 1;
+         break;
+       case 'f':
+         flag_function_summary = 1;
+         break;
+       case 'h':
+         print_usage (false);
+         /* print_usage will exit.  */
+       case 'l':
+         flag_long_names = 1;
+         break;
+       case 'n':
+         flag_gcov_file = 0;
+         break;
+       case 'o':
+         object_directory = optarg;
+         break;
+       case 's':
+         source_prefix = optarg;
+         source_length = strlen (source_prefix);
+         break;
+       case 'r':
+         flag_relative_only = 1;
+         break;
+       case 'p':
+         flag_preserve_paths = 1;
+         break;
+       case 'u':
+         flag_unconditional = 1;
+         break;
+        case 'd':
+          flag_display_progress = 1;
+          break;
+       case 'v':
+         print_version ();
+         /* print_version will exit.  */
+       default:
+         print_usage (true);
+         /* print_usage will exit.  */
        }
-      else if (! input_file_name)
-       input_file_name = argv[i];
-      else
-       print_usage ();
     }
 
-  if (! input_file_name)
-    print_usage ();
+  return optind;
 }
 
-
-/* Find and open the .bb, .da, and .bbg files.  */
+/* Process a single input file.  */
 
 static void
-open_files ()
+process_file (const char *file_name)
 {
-  int count, objdir_count;
-  char *cptr;
-
-  /* Determine the names of the .bb, .bbg, and .da files.  Strip off the
-     extension, if any, and append the new extensions.  */
-  count = strlen (input_file_name);
-  if (object_directory)
-    objdir_count = strlen (object_directory);
-  else
-    objdir_count = 0;
-
-  da_file_name = xmalloc (count + objdir_count + 4);
-  bb_file_name = xmalloc (count + objdir_count + 4);
-  bbg_file_name = xmalloc (count + objdir_count + 5);
-
-  if (object_directory)
+  function_t *fns;
+
+  create_file_names (file_name);
+  fns = read_graph_file ();
+  if (!fns)
+    return;
+  
+  read_count_file (fns);
+  while (fns)
     {
-      strcpy (da_file_name, object_directory);
-      strcpy (bb_file_name, object_directory);
-      strcpy (bbg_file_name, object_directory);
+      function_t *fn = fns;
 
-      if (object_directory[objdir_count - 1] != '/')
+      fns = fn->next;
+      fn->next = NULL;
+      if (fn->counts)
        {
-         strcat (da_file_name, "/");
-         strcat (bb_file_name, "/");
-         strcat (bbg_file_name, "/");
-       }
+         unsigned src = fn->src;
+         unsigned line = fn->line;
+         unsigned block_no;
+         function_t *probe, **prev;
+         
+         /* Now insert it into the source file's list of
+            functions. Normally functions will be encountered in
+            ascending order, so a simple scan is quick.  Note we're
+            building this list in reverse order.  */
+         for (prev = &sources[src].functions;
+              (probe = *prev); prev = &probe->line_next)
+           if (probe->line <= line)
+             break;
+         fn->line_next = probe;
+         *prev = fn;
+
+         /* Mark last line in files touched by function.  */
+         for (block_no = 0; block_no != fn->num_blocks; block_no++)
+           {
+             unsigned *enc = fn->blocks[block_no].u.line.encoding;
+             unsigned num = fn->blocks[block_no].u.line.num;
 
-      cptr = strrchr (input_file_name, '/');
-      if (cptr)
-       {
-         strcat (da_file_name, cptr + 1);
-         strcat (bb_file_name, cptr + 1);
-         strcat (bbg_file_name, cptr + 1);
+             for (; num--; enc++)
+               if (!*enc)
+                 {
+                   if (enc[1] != src)
+                     {
+                       if (line >= sources[src].num_lines)
+                         sources[src].num_lines = line + 1;
+                       line = 0;
+                       src = enc[1];
+                     }
+                   enc++;
+                   num--;
+                 }
+               else if (*enc > line)
+                 line = *enc;
+           }
+         if (line >= sources[src].num_lines)
+           sources[src].num_lines = line + 1;
+         
+         solve_flow_graph (fn);
+         if (fn->has_catch)
+           find_exception_blocks (fn);
+         *fn_end = fn;
+         fn_end = &fn->next;
        }
       else
-       {
-         strcat (da_file_name, input_file_name);
-         strcat (bb_file_name, input_file_name);
-         strcat (bbg_file_name, input_file_name);
-       }
-    }
-  else
-    {
-      strcpy (da_file_name, input_file_name);
-      strcpy (bb_file_name, input_file_name);
-      strcpy (bbg_file_name, input_file_name);
+       /* The function was not in the executable -- some other
+          instance must have been selected.  */
+       release_function (fn);
     }
+}
 
-  cptr = strrchr (bb_file_name, '.');
-  if (cptr)
-    strcpy (cptr, ".bb");
-  else
-    strcat (bb_file_name, ".bb");
-
-  cptr = strrchr (da_file_name, '.');
-  if (cptr)
-    strcpy (cptr, ".da");
-  else
-    strcat (da_file_name, ".da");
+static void
+generate_results (const char *file_name)
+{
+  unsigned ix;
+  source_t *src;
+  function_t *fn;
 
-  cptr = strrchr (bbg_file_name, '.');
-  if (cptr)
-    strcpy (cptr, ".bbg");
-  else
-    strcat (bbg_file_name, ".bbg");
+  for (ix = n_sources, src = sources; ix--; src++)
+    if (src->num_lines)
+      src->lines = XCNEWVEC (line_t, src->num_lines);
 
-  bb_file = fopen (bb_file_name, "rb");
-  if (bb_file == NULL)
+  for (fn = functions; fn; fn = fn->next)
     {
-      fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name);
-      exit (FATAL_EXIT_CODE);
-    }
+      coverage_t coverage;
 
-  /* If none of the functions in the file were executed, then there won't
-     be a .da file.  Just assume that all counts are zero in this case.  */
-  da_file = fopen (da_file_name, "rb");
-  if (da_file == NULL)
-    {
-      fnotice (stderr, "Could not open data file %s.\n", da_file_name);
-      fnotice (stderr, "Assuming that all execution counts are zero.\n");
+      memset (&coverage, 0, sizeof (coverage));
+      coverage.name = fn->name;
+      add_line_counts (flag_function_summary ? &coverage : NULL, fn);
+      if (flag_function_summary)
+       {
+         function_summary (&coverage, "Function");
+         fnotice (stdout, "\n");
+       }
     }
 
-  bbg_file = fopen (bbg_file_name, "rb");
-  if (bbg_file == NULL)
+  if (file_name)
     {
-      fnotice (stderr, "Could not open program flow graph file %s.\n",
-              bbg_file_name);
-      exit (FATAL_EXIT_CODE);
+      name_map_t *name_map = (name_map_t *)bsearch
+       (file_name, names, n_names, sizeof (*names), name_search);
+      if (name_map)
+       file_name = sources[name_map->src].coverage.name;
+      else
+       file_name = canonicalize_name (file_name);
     }
-
-  /* Check for empty .bbg file.  This indicates that there is no executable
-     code in this source file.  */
-  /* Set the EOF condition if at the end of file.  */
-  ungetc (getc (bbg_file), bbg_file);
-  if (feof (bbg_file))
+  
+  for (ix = n_sources, src = sources; ix--; src++)
     {
-      fnotice (stderr, "No executable code associated with file %s.\n",
-              input_file_name);
-      exit (FATAL_EXIT_CODE);
+      if (flag_relative_only)
+       {
+         /* Ignore this source, if it is an absolute path (after
+            source prefix removal).  */
+         char first = src->coverage.name[0];
+      
+#if HAVE_DOS_BASED_FILE_SYSTEM
+         if (first && src->coverage.name[1] == ':')
+           first = src->coverage.name[2];
+#endif
+         if (IS_DIR_SEPARATOR (first))
+           continue;
+       }
+      
+      accumulate_line_counts (src);
+      function_summary (&src->coverage, "File");
+      total_lines += src->coverage.lines;
+      total_executed += src->coverage.lines_executed;
+      if (flag_gcov_file)
+       {
+         char *gcov_file_name
+           = make_gcov_file_name (file_name, src->coverage.name);
+
+         if (src->coverage.lines)
+           {
+             FILE *gcov_file = fopen (gcov_file_name, "w");
+
+             if (gcov_file)
+               {
+                 fnotice (stdout, "Creating '%s'\n", gcov_file_name);
+                 output_lines (gcov_file, src);
+                 if (ferror (gcov_file))
+                   fnotice (stderr, "Error writing output file '%s'\n",
+                            gcov_file_name);
+                 fclose (gcov_file);
+               }
+             else
+               fnotice (stderr, "Could not open output file '%s'\n",
+                        gcov_file_name);
+           }
+         else
+           {
+             unlink (gcov_file_name);
+             fnotice (stdout, "Removing '%s'\n", gcov_file_name);
+           }
+         free (gcov_file_name);
+       }
+      fnotice (stdout, "\n");
     }
+
+  if (!file_name)
+    executed_summary (total_lines, total_executed);
 }
-\f
-/* Initialize a new arc.  */
+
+/* Release a function structure */
 
 static void
-init_arc (arcptr, source, target, bb_graph)
-     struct adj_list *arcptr;
-     int source, target;
-     struct bb_info *bb_graph;
+release_function (function_t *fn)
 {
-  arcptr->target = target;
-  arcptr->source = source;
-
-  arcptr->arc_count = 0;
-  arcptr->count_valid = 0;
-  arcptr->on_tree = 0;
-  arcptr->fake = 0;
-  arcptr->fall_through = 0;
-
-  arcptr->succ_next = bb_graph[source].succ;
-  bb_graph[source].succ = arcptr;
-  bb_graph[source].succ_count++;
-
-  arcptr->pred_next = bb_graph[target].pred;
-  bb_graph[target].pred = arcptr;
-  bb_graph[target].pred_count++;
-}
+  unsigned ix;
+  block_t *block;
+
+  for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
+    {
+      arc_t *arc, *arc_n;
 
+      for (arc = block->succ; arc; arc = arc_n)
+       {
+         arc_n = arc->succ_next;
+         free (arc);
+       }
+    }
+  free (fn->blocks);
+  free (fn->counts);
+}
 
-/* Reverse the arcs on an arc list.  */
+/* Release all memory used.  */
 
-static struct adj_list *
-reverse_arcs (arcptr)
-     struct adj_list *arcptr;
+static void
+release_structures (void)
 {
-  struct adj_list *prev = 0;
-  struct adj_list *next;
-
-  for ( ; arcptr; arcptr = next)
+  unsigned ix;
+  function_t *fn;
+
+  for (ix = n_sources; ix--;)
+    free (sources[ix].lines);
+  free (sources);
+  
+  for (ix = n_names; ix--;)
+    free (names[ix].name);
+  free (names);
+
+  while ((fn = functions))
     {
-      next = arcptr->succ_next;
-      arcptr->succ_next = prev;
-      prev = arcptr;
+      functions = fn->next;
+      release_function (fn);
     }
-
-  return prev;
 }
 
-
-/* Construct the program flow graph from the .bbg file, and read in the data
-   in the .da file.  */
+/* Generate the names of the graph and data files.  If OBJECT_DIRECTORY
+   is not specified, these are named from FILE_NAME sans extension.  If
+   OBJECT_DIRECTORY is specified and is a directory, the files are in that
+   directory, but named from the basename of the FILE_NAME, sans extension.
+   Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file*
+   and the data files are named from that.  */
 
 static void
-create_program_flow_graph (bptr)
-     struct bb_info_list *bptr;
+create_file_names (const char *file_name)
 {
-  long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block;
-  int i;
-  struct adj_list *arcptr;
-  struct bb_info *bb_graph;
+  char *cptr;
+  char *name;
+  int length = strlen (file_name);
+  int base;
 
-  /* Read the number of blocks.  */
-  __read_long (&num_blocks, bbg_file, 4);
+  /* Free previous file names.  */
+  free (bbg_file_name);
+  free (da_file_name);
+  da_file_name = bbg_file_name = NULL;
+  bbg_file_time = 0;
+  bbg_stamp = 0;
 
-  /* Create an array of size bb number of bb_info structs.  */
-  bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info));
+  if (object_directory && object_directory[0])
+    {
+      struct stat status;
 
-  bptr->bb_graph = bb_graph;
-  bptr->num_blocks = num_blocks;
+      length += strlen (object_directory) + 2;
+      name = XNEWVEC (char, length);
+      name[0] = 0;
 
-  /* Read and create each arc from the .bbg file.  */
-  __read_long (&number_arcs, bbg_file, 4);
-  for (i = 0; i < num_blocks; i++)
+      base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
+      strcat (name, object_directory);
+      if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
+       strcat (name, "/");
+    }
+  else
     {
-      int j;
+      name = XNEWVEC (char, length + 1);
+      strcpy (name, file_name);
+      base = 0;
+    }
 
-      __read_long (&num_arcs_per_block, bbg_file, 4);
-      for (j = 0; j < num_arcs_per_block; j++)
-       {
-         if (number_arcs-- < 0)
-           abort ();
+  if (base)
+    {
+      /* Append source file name.  */
+      const char *cptr = lbasename (file_name);
+      strcat (name, cptr ? cptr : file_name);
+    }
 
-         src = i;
-         __read_long (&dest, bbg_file, 4);
+  /* Remove the extension.  */
+  cptr = strrchr (name, '.');
+  if (cptr)
+    *cptr = 0;
 
-         arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list));
-         init_arc (arcptr, src, dest, bb_graph);
+  length = strlen (name);
 
-         __read_long (&flag_bits, bbg_file, 4);
-         arcptr->on_tree = flag_bits & 0x1;
-         arcptr->fake = !! (flag_bits & 0x2);
-         arcptr->fall_through = !! (flag_bits & 0x4);
-       }
-    }
+  bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
+  strcpy (bbg_file_name, name);
+  strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
+
+  da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
+  strcpy (da_file_name, name);
+  strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
 
-  if (number_arcs)
-    abort ();
+  free (name);
+  return;
+}
+
+/* A is a string and B is a pointer to name_map_t.  Compare for file
+   name orderability.  */
 
-  /* Read and ignore the -1 separating the arc list from the arc list of the
-     next function.  */
-  __read_long (&src, bbg_file, 4);
-  if (src != -1)
-    abort ();
+static int
+name_search (const void *a_, const void *b_)
+{
+  const char *a = (const char *)a_;
+  const name_map_t *b = (const name_map_t *)b_;
 
-  /* Must reverse the order of all succ arcs, to ensure that they match
-     the order of the data in the .da file.  */
+#if HAVE_DOS_BASED_FILE_SYSTEM
+  return strcasecmp (a, b->name);
+#else
+  return strcmp (a, b->name);
+#endif
+}
 
-  for (i = 0; i < num_blocks; i++)
-    if (bb_graph[i].succ)
-      bb_graph[i].succ = reverse_arcs (bb_graph[i].succ);
+/* A and B are a pointer to name_map_t.  Compare for file name
+   orderability.  */
 
-  /* For each arc not on the spanning tree, set its execution count from
-     the .da file.  */
+static int
+name_sort (const void *a_, const void *b_)
+{
+  const name_map_t *a = (const name_map_t *)a_;
+  return name_search (a->name, b_);
+}
 
-  /* The first count in the .da file is the number of times that the function
-     was entered.  This is the exec_count for block zero.  */
+/* Find or create a source file structure for FILE_NAME. Copies
+   FILE_NAME on creation */
 
-  /* This duplicates code in branch_prob in profile.c.  */
+static unsigned
+find_source (const char *file_name)
+{
+  name_map_t *name_map;
+  char *canon;
+  unsigned idx;
+  struct stat status;
+
+  if (!file_name)
+    file_name = "<unknown>";
+  name_map = (name_map_t *)bsearch
+    (file_name, names, n_names, sizeof (*names), name_search);
+  if (name_map)
+    {
+      idx = name_map->src;
+      goto check_date;
+    }
 
-  for (i = 0; i < num_blocks; i++)
-    for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
-      if (! arcptr->on_tree)
+  if (n_names + 2 > a_names)
+    {
+      /* Extend the name map array -- we'll be inserting one or two
+        entries.  */
+      a_names *= 2;
+      name_map = XNEWVEC (name_map_t, a_names);
+      memcpy (name_map, names, n_names * sizeof (*names));
+      free (names);
+      names = name_map;
+    }
+  
+  /* Not found, try the canonical name. */
+  canon = canonicalize_name (file_name);
+  name_map = (name_map_t *)bsearch
+    (canon, names, n_names, sizeof (*names), name_search);
+  if (!name_map)
+    {
+      /* Not found with canonical name, create a new source.  */
+      source_t *src;
+      
+      if (n_sources == a_sources)
        {
-         gcov_type tmp_count = 0;
-         if (da_file && __read_gcov_type (&tmp_count, da_file, 8))
-           abort();
-
-         arcptr->arc_count = tmp_count;
-         arcptr->count_valid = 1;
-         bb_graph[i].succ_count--;
-         bb_graph[arcptr->target].pred_count--;
+         a_sources *= 2;
+         src = XNEWVEC (source_t, a_sources);
+         memcpy (src, sources, n_sources * sizeof (*sources));
+         free (sources);
+         sources = src;
        }
-}
 
-static void
-solve_program_flow_graph (bptr)
-     struct bb_info_list *bptr;
-{
-  int passes, changes;
-  gcov_type total;
-  int i;
-  struct adj_list *arcptr;
-  struct bb_info *bb_graph;
-  int num_blocks;
-
-  num_blocks = bptr->num_blocks;
-  bb_graph = bptr->bb_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)
+      idx = n_sources;
+
+      name_map = &names[n_names++];
+      name_map->name = canon;
+      name_map->src = idx;
+
+      src = &sources[n_sources++];
+      memset (src, 0, sizeof (*src));
+      src->name = canon;
+      src->coverage.name = src->name;
+      if (source_length
+#if HAVE_DOS_BASED_FILE_SYSTEM
+         /* You lose if separators don't match exactly in the
+            prefix.  */
+         && !strncasecmp (source_prefix, src->coverage.name, source_length)
+#else
+         && !strncmp (source_prefix, src->coverage.name, source_length)
+#endif
+         && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
+       src->coverage.name += source_length + 1;
+      if (!stat (src->name, &status))
+       src->file_time = status.st_mtime;
+    }
+  else
+    idx = name_map->src;
+
+  if (name_search (file_name, name_map))
+    {
+      /* Append the non-canonical name.  */
+      name_map = &names[n_names++];
+      name_map->name = xstrdup (file_name);
+      name_map->src = idx;
+    }
+
+  /* Resort the name map.  */
+  qsort (names, n_names, sizeof (*names), name_sort);
+  
+ check_date:
+  if (sources[idx].file_time > bbg_file_time)
     {
-      passes++;
-      changes = 0;
+      static int info_emitted;
 
-      for (i = num_blocks - 1; i >= 0; i--)
+      fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
+              file_name, bbg_file_name);
+      if (!info_emitted)
        {
-         if (! bb_graph[i].count_valid)
-           {
-             if (bb_graph[i].succ_count == 0)
-               {
-                 total = 0;
-                 for (arcptr = bb_graph[i].succ; arcptr;
-                      arcptr = arcptr->succ_next)
-                   total += arcptr->arc_count;
-                 bb_graph[i].exec_count = total;
-                 bb_graph[i].count_valid = 1;
-                 changes = 1;
-               }
-             else if (bb_graph[i].pred_count == 0)
-               {
-                 total = 0;
-                 for (arcptr = bb_graph[i].pred; arcptr;
-                      arcptr = arcptr->pred_next)
-                   total += arcptr->arc_count;
-                 bb_graph[i].exec_count = total;
-                 bb_graph[i].count_valid = 1;
-                 changes = 1;
-               }
-           }
-         if (bb_graph[i].count_valid)
-           {
-             if (bb_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 (arcptr = bb_graph[i].succ; arcptr;
-                      arcptr = arcptr->succ_next)
-                   total += arcptr->arc_count;
-                 /* Calculate count for remaining arc by conservation.  */
-                 total = bb_graph[i].exec_count - total;
-                 /* Search for the invalid arc, and set its count.  */
-                 for (arcptr = bb_graph[i].succ; arcptr;
-                      arcptr = arcptr->succ_next)
-                   if (! arcptr->count_valid)
-                     break;
-                 if (! arcptr)
-                   abort ();
-                 arcptr->count_valid = 1;
-                 arcptr->arc_count = total;
-                 bb_graph[i].succ_count--;
-
-                 bb_graph[arcptr->target].pred_count--;
-                 changes = 1;
-               }
-             if (bb_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 (arcptr = bb_graph[i].pred; arcptr;
-                      arcptr = arcptr->pred_next)
-                   total += arcptr->arc_count;
-                 /* Calculate count for remaining arc by conservation.  */
-                 total = bb_graph[i].exec_count - total;
-                 /* Search for the invalid arc, and set its count.  */
-                 for (arcptr = bb_graph[i].pred; arcptr;
-                      arcptr = arcptr->pred_next)
-                   if (! arcptr->count_valid)
-                     break;
-                 if (! arcptr)
-                   abort ();
-                 arcptr->count_valid = 1;
-                 arcptr->arc_count = total;
-                 bb_graph[i].pred_count--;
-
-                 bb_graph[arcptr->source].succ_count--;
-                 changes = 1;
-               }
-           }
+         fnotice (stderr,
+                  "(the message is only displayed one per source file)\n");
+         info_emitted = 1;
        }
+      sources[idx].file_time = 0;
     }
 
-  /* If the graph has been correctly solved, every block will have a
-     succ and pred count of zero.  */
-  for (i = 0; i < num_blocks; i++)
-    if (bb_graph[i].succ_count || bb_graph[i].pred_count)
-      abort ();
+  return idx;
 }
 
+/* Read the graph file.  Return list of functions read -- in reverse order.  */
 
-static void
-read_files ()
+static function_t *
+read_graph_file (void)
 {
-  struct stat buf;
-  struct bb_info_list *list_end = 0;
-  struct bb_info_list *b_ptr;
-  long total;
-
-  /* Read and ignore the first word of the .da file, which is the count of
-     how many numbers follow.  */
-  if (da_file && __read_long (&total, da_file, 8))
-    abort();
-
-  while (! feof (bbg_file))
+  unsigned version;
+  unsigned current_tag = 0;
+  function_t *fn = NULL;
+  function_t *fns = NULL;
+  function_t **fns_end = &fns;
+  unsigned src_idx = 0;
+  unsigned ix;
+  unsigned tag;
+
+  if (!gcov_open (bbg_file_name, 1))
+    {
+      fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
+      return fns;
+    }
+  bbg_file_time = gcov_time ();
+  if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
     {
-      b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list));
+      fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
+      gcov_close ();
+      return fns;
+    }
 
-      b_ptr->next = 0;
-      if (list_end)
-       list_end->next = b_ptr;
-      else
-       bb_graph_list = b_ptr;
-      list_end = b_ptr;
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
+    {
+      char v[4], e[4];
 
-      /* Read in the data in the .bbg file and reconstruct the program flow
-        graph for one function.  */
-      create_program_flow_graph (b_ptr);
+      GCOV_UNSIGNED2STRING (v, version);
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
-      /* Set the EOF condition if at the end of file.  */
-      ungetc (getc (bbg_file), bbg_file);
+      fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
+              bbg_file_name, v, e);
     }
+  bbg_stamp = gcov_read_unsigned ();
 
-  /* Check to make sure the .da file data is valid.  */
-
-  if (da_file)
+  while ((tag = gcov_read_unsigned ()))
     {
-      if (feof (da_file))
-       fnotice (stderr, ".da file contents exhausted too early\n");
-      /* Should be at end of file now.  */
-      if (__read_long (&total, da_file, 8) == 0)
-       fnotice (stderr, ".da file contents not exhausted\n");
-    }
+      unsigned length = gcov_read_unsigned ();
+      gcov_position_t base = gcov_position ();
 
-  /* Calculate all of the basic block execution counts and branch
-     taken probabilities.  */
+      if (tag == GCOV_TAG_FUNCTION)
+       {
+         char *function_name;
+         unsigned ident, lineno;
+         unsigned lineno_checksum, cfg_checksum;
+
+         ident = gcov_read_unsigned ();
+         lineno_checksum = gcov_read_unsigned ();
+         cfg_checksum = gcov_read_unsigned ();
+         function_name = xstrdup (gcov_read_string ());
+         src_idx = find_source (gcov_read_string ());
+         lineno = gcov_read_unsigned ();
+
+         fn = XCNEW (function_t);
+         fn->name = function_name;
+         fn->ident = ident;
+         fn->lineno_checksum = lineno_checksum;
+         fn->cfg_checksum = cfg_checksum;
+         fn->src = src_idx;
+         fn->line = lineno;
+
+         fn->line_next = NULL;
+         fn->next = NULL;
+         *fns_end = fn;
+         fns_end = &fn->next;
+         current_tag = tag;
+       }
+      else if (fn && tag == GCOV_TAG_BLOCKS)
+       {
+         if (fn->blocks)
+           fnotice (stderr, "%s:already seen blocks for '%s'\n",
+                    bbg_file_name, fn->name);
+         else
+           {
+             unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
+             fn->num_blocks = num_blocks;
 
-  for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next)
-    solve_program_flow_graph (b_ptr);
+             fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
+             for (ix = 0; ix != num_blocks; ix++)
+               fn->blocks[ix].flags = gcov_read_unsigned ();
+           }
+       }
+      else if (fn && tag == GCOV_TAG_ARCS)
+       {
+         unsigned src = gcov_read_unsigned ();
+         unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
+         block_t *src_blk = &fn->blocks[src];
+         unsigned mark_catches = 0;
+         struct arc_info *arc;
 
-  /* Read in all of the data from the .bb file.   This info will be accessed
-     sequentially twice.  */
-  stat (bb_file_name, &buf);
-  bb_data_size = buf.st_size / 4;
+         if (src >= fn->num_blocks || fn->blocks[src].succ)
+           goto corrupt;
 
-  bb_data = (char *) xmalloc ((unsigned) buf.st_size);
-  fread (bb_data, sizeof (char), buf.st_size, bb_file);
+         while (num_dests--)
+           {
+             unsigned dest = gcov_read_unsigned ();
+             unsigned flags = gcov_read_unsigned ();
 
-  fclose (bb_file);
-  if (da_file)
-    fclose (da_file);
-  fclose (bbg_file);
-}
+             if (dest >= fn->num_blocks)
+               goto corrupt;
+             arc = XCNEW (arc_t);
 
+             arc->dst = &fn->blocks[dest];
+             arc->src = src_blk;
 
-/* Scan the data in the .bb file to find all source files referenced,
-   and the largest line number mentioned in each one.  */
+             arc->count = 0;
+             arc->count_valid = 0;
+             arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
+             arc->fake = !!(flags & GCOV_ARC_FAKE);
+             arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
 
-static void
-scan_for_source_files ()
-{
-  struct sourcefile *s_ptr = NULL;
-  char *ptr;
-  long count;
-  long line_num;
+             arc->succ_next = src_blk->succ;
+             src_blk->succ = arc;
+             src_blk->num_succ++;
 
-  /* Search the bb_data to find:
-     1) The number of sources files contained herein, and
-     2) The largest line number for each source file.  */
+             arc->pred_next = fn->blocks[dest].pred;
+             fn->blocks[dest].pred = arc;
+             fn->blocks[dest].num_pred++;
 
-  ptr = bb_data;
-  sources = 0;
-  for (count = 0; count < bb_data_size; count++)
-    {
-      __fetch_long (&line_num, ptr, 4);
-      ptr += 4;
-      if (line_num == -1)
-       {
-         /* A source file name follows.  Check to see if we already have
-          a sourcefile structure for this file.  */
-         s_ptr = sources;
-         while (s_ptr && strcmp (s_ptr->name, ptr))
-           s_ptr = s_ptr->next;
+             if (arc->fake)
+               {
+                 if (src)
+                   {
+                     /* Exceptional exit from this function, the
+                        source block must be a call.  */
+                     fn->blocks[src].is_call_site = 1;
+                     arc->is_call_non_return = 1;
+                     mark_catches = 1;
+                   }
+                 else
+                   {
+                     /* Non-local return from a callee of this
+                        function. The destination block is a setjmp.  */
+                     arc->is_nonlocal_return = 1;
+                     fn->blocks[dest].is_nonlocal_return = 1;
+                   }
+               }
 
-         if (s_ptr == 0)
-           {
-             /* No sourcefile structure for this file name exists, create
-                a new one, and append it to the front of the sources list.  */
-             s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile));
-             s_ptr->name = xstrdup (ptr);
-             s_ptr->maxlineno = 0;
-             s_ptr->next = sources;
-             sources = s_ptr;
+             if (!arc->on_tree)
+               fn->num_counts++;
            }
+         
+         if (mark_catches)
+           {
+             /* We have a fake exit from this block.  The other
+                non-fall through exits must be to catch handlers.
+                Mark them as catch arcs.  */
 
-         /* Scan past the file name.  */
-         {
-           long delim;
-           do {
-             count++;
-             __fetch_long (&delim, ptr, 4);
-             ptr += 4;
-           } while (delim != line_num);
-         }
+             for (arc = src_blk->succ; arc; arc = arc->succ_next)
+               if (!arc->fake && !arc->fall_through)
+                 {
+                   arc->is_throw = 1;
+                   fn->has_catch = 1;
+                 }
+           }
        }
-      else if (line_num == -2)
+      else if (fn && tag == GCOV_TAG_LINES)
        {
-         long delim;
-
-         /* A function name follows.  Ignore it.  */
-         do {
-           count++;
-           __fetch_long (&delim, ptr, 4);
-           ptr += 4;
-         } while (delim != line_num);
+         unsigned blockno = gcov_read_unsigned ();
+         unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
+
+         if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
+           goto corrupt;
+
+         for (ix = 0; ;  )
+           {
+             unsigned lineno = gcov_read_unsigned ();
+
+             if (lineno)
+               {
+                 if (!ix)
+                   {
+                     line_nos[ix++] = 0;
+                     line_nos[ix++] = src_idx;
+                   }
+                 line_nos[ix++] = lineno;
+               }
+             else
+               {
+                 const char *file_name = gcov_read_string ();
+
+                 if (!file_name)
+                   break;
+                 src_idx = find_source (file_name);
+                 line_nos[ix++] = 0;
+                 line_nos[ix++] = src_idx;
+               }
+           }
+
+         fn->blocks[blockno].u.line.encoding = line_nos;
+         fn->blocks[blockno].u.line.num = ix;
        }
-      /* There will be a zero before the first file name, in which case s_ptr
-        will still be uninitialized.  So, only try to set the maxlineno
-        field if line_num is non-zero.  */
-      else if (line_num > 0)
+      else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
        {
-         if (s_ptr->maxlineno <= line_num)
-           s_ptr->maxlineno = line_num + 1;
+         fn = NULL;
+         current_tag = 0;
        }
-      else if (line_num < 0)
+      gcov_sync (base, length);
+      if (gcov_is_error ())
        {
-         /* Don't know what this is, but it's garbage.  */
-         abort();
+       corrupt:;
+         fnotice (stderr, "%s:corrupted\n", bbg_file_name);
+         break;
        }
     }
-}
-\f
-/* For calculating coverage at the function level.  */
+  gcov_close ();
 
-static int function_source_lines;
-static int function_source_lines_executed;
-static int function_branches;
-static int function_branches_executed;
-static int function_branches_taken;
-static int function_calls;
-static int function_calls_executed;
-static char *function_name;
+  if (!fns)
+    fnotice (stderr, "%s:no functions found\n", bbg_file_name);
 
-/* Calculate the branch taken probabilities for all arcs branches at the
-   end of this block.  */
+  return fns;
+}
 
-static void
-calculate_branch_probs (current_graph, block_num, branch_probs, last_line_num)
-     struct bb_info_list *current_graph;
-     int block_num;
-     struct arcdata **branch_probs;
-     int last_line_num;
+/* Reads profiles from the count file and attach to each
+   function. Return nonzero if fatal error.  */
+
+static int
+read_count_file (function_t *fns)
 {
-  gcov_type total;
-  struct adj_list *arcptr;
-  struct arcdata *end_ptr, *a_ptr;
+  unsigned ix;
+  unsigned version;
+  unsigned tag;
+  function_t *fn = NULL;
+  int error = 0;
 
-  total = current_graph->bb_graph[block_num].exec_count;
-  for (arcptr = current_graph->bb_graph[block_num].succ; arcptr;
-       arcptr = arcptr->succ_next)
+  if (!gcov_open (da_file_name, 1))
+    {
+      fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
+              da_file_name);
+      no_data_file = 1;
+      return 0;
+    }
+  if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
     {
-      /* Ignore fall through arcs as they aren't really branches.  */
+      fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
+    cleanup:;
+      gcov_close ();
+      return 1;
+    }
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
+    {
+      char v[4], e[4];
 
-      if (arcptr->fall_through)
-       continue;
+      GCOV_UNSIGNED2STRING (v, version);
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
-      a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
-      a_ptr->total = total;
-      if (total == 0)
-          a_ptr->hits = 0;
-      else
-          a_ptr->hits = arcptr->arc_count;
-      a_ptr->call_insn = arcptr->fake;
+      fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
+              da_file_name, v, e);
+    }
+  tag = gcov_read_unsigned ();
+  if (tag != bbg_stamp)
+    {
+      fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
+      goto cleanup;
+    }
+
+  while ((tag = gcov_read_unsigned ()))
+    {
+      unsigned length = gcov_read_unsigned ();
+      unsigned long base = gcov_position ();
 
-      if (output_function_summary)
+      if (tag == GCOV_TAG_PROGRAM_SUMMARY)
+       {
+         struct gcov_summary summary;
+         gcov_read_summary (&summary);
+         object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
+         program_count++;
+       }
+      else if (tag == GCOV_TAG_FUNCTION && !length)
+       ; /* placeholder  */
+      else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
        {
-         if (a_ptr->call_insn)
+         unsigned ident;
+         struct function_info *fn_n;
+
+         /* Try to find the function in the list.  To speed up the
+            search, first start from the last function found.  */
+         ident = gcov_read_unsigned ();
+         fn_n = fns;
+         for (fn = fn ? fn->next : NULL; ; fn = fn->next)
            {
-             function_calls++;
-             if (a_ptr->total != 0)
-               function_calls_executed++;
+             if (fn)
+               ;
+             else if ((fn = fn_n))
+               fn_n = NULL;
+             else
+               {
+                 fnotice (stderr, "%s:unknown function '%u'\n",
+                          da_file_name, ident);
+                 break;
+               }
+             if (fn->ident == ident)
+               break;
            }
-         else
+
+         if (!fn)
+           ;
+         else if (gcov_read_unsigned () != fn->lineno_checksum
+                  || gcov_read_unsigned () != fn->cfg_checksum)
            {
-             function_branches++;
-             if (a_ptr->total != 0)
-               function_branches_executed++;
-             if (a_ptr->hits > 0)
-               function_branches_taken++;
+           mismatch:;
+             fnotice (stderr, "%s:profile mismatch for '%s'\n",
+                      da_file_name, fn->name);
+             goto cleanup;
            }
        }
-
-      /* Append the new branch to the end of the list.  */
-      a_ptr->next = 0;
-      if (! branch_probs[last_line_num])
-       branch_probs[last_line_num] = a_ptr;
-      else
+      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
        {
-         end_ptr = branch_probs[last_line_num];
-         while (end_ptr->next != 0)
-           end_ptr = end_ptr->next;
-         end_ptr->next = a_ptr;
-       }
-    }
-}
+         if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
+           goto mismatch;
 
-/* Output summary info for a function.  */
+         if (!fn->counts)
+           fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
 
-static void
-function_summary ()
-{
-  if (function_source_lines)
-    fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n",
-            (((double) function_source_lines_executed / function_source_lines)
-             * 100), function_source_lines, function_name);
-  else
-    fnotice (stdout, "No executable source lines in function %s\n",
-            function_name);
-
-  if (output_branch_probs)
-    {
-      if (function_branches)
+         for (ix = 0; ix != fn->num_counts; ix++)
+           fn->counts[ix] += gcov_read_counter ();
+       }
+      gcov_sync (base, length);
+      if ((error = gcov_is_error ()))
        {
-         fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n",
-                  (((double) function_branches_executed / function_branches)
-                   * 100), function_branches, function_name);
-         fnotice (stdout,
-               "%6.2f%% of %d branches taken at least once in function %s\n",
-                  (((double) function_branches_taken / function_branches)
-                   * 100), function_branches, function_name);
+         fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
+                  da_file_name);
+         goto cleanup;
        }
-      else
-       fnotice (stdout, "No branches in function %s\n", function_name);
-      if (function_calls)
-       fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n",
-                (((double) function_calls_executed / function_calls)
-                 * 100), function_calls, function_name);
-      else
-       fnotice (stdout, "No calls in function %s\n", function_name);
     }
+
+  gcov_close ();
+  return 0;
 }
 
-/* Calculate line execution counts, and output the data to a .tcov file.  */
+/* Solve the flow graph. Propagate counts from the instrumented arcs
+   to the blocks and the uninstrumented arcs.  */
 
 static void
-output_data ()
+solve_flow_graph (function_t *fn)
 {
-  /* When scanning data, this is true only if the data applies to the
-     current source file.  */
-  int this_file;
-  /* An array indexed by line number which indicates how many times that line
-     was executed.  */
-  gcov_type *line_counts;
-  /* An array indexed by line number which indicates whether the line was
-     present in the bb file (i.e. whether it had code associate with it).
-     Lines never executed are those which both exist, and have zero execution
-     counts.  */
-  char *line_exists;
-  /* An array indexed by line number, which contains a list of branch
-     probabilities, one for each branch on that line.  */
-  struct arcdata **branch_probs = NULL;
-  struct sourcefile *s_ptr;
-  char *source_file_name;
-  FILE *source_file;
-  struct bb_info_list *current_graph;
-  long count;
-  char *cptr;
-  long block_num;
-  long line_num;
-  long last_line_num = 0;
-  int i;
-  struct arcdata *a_ptr;
-  /* Buffer used for reading in lines from the source file.  */
-  char string[STRING_SIZE];
-  /* For calculating coverage at the file level.  */
-  int total_source_lines;
-  int total_source_lines_executed;
-  int total_branches;
-  int total_branches_executed;
-  int total_branches_taken;
-  int total_calls;
-  int total_calls_executed;
-
-  /* Now, for each source file, allocate an array big enough to hold a count
-     for each line.  Scan through the bb_data, and when the file name matches
-     the current file name, then for each following line number, increment
-     the line number execution count indicated by the execution count of
-     the appropriate basic block.  */
-
-  for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
+  unsigned ix;
+  arc_t *arc;
+  gcov_type *count_ptr = fn->counts;
+  block_t *blk;
+  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
+  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
+
+  /* The arcs were built in reverse order.  Fix that now.  */
+  for (ix = fn->num_blocks; ix--;)
     {
-      /* If this is a relative file name, and an object directory has been
-        specified, then make it relative to the object directory name.  */
-      if (! (*s_ptr->name == '/' || *s_ptr->name == DIR_SEPARATOR
-            /* Check for disk name on MS-DOS-based systems.  */
-            || (DIR_SEPARATOR == '\\'
-                && s_ptr->name[1] == ':'
-                && (s_ptr->name[2] == DIR_SEPARATOR
-                    || s_ptr->name[2] == '/')))
-         && object_directory != 0
-         && *object_directory != '\0')
+      arc_t *arc_p, *arc_n;
+
+      for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
+          arc_p = arc, arc = arc_n)
        {
-         int objdir_count = strlen (object_directory);
-         source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2);
-         strcpy (source_file_name, object_directory);
-         if (object_directory[objdir_count - 1] != '/')
-           source_file_name[objdir_count++] = '/';
-         strcpy (source_file_name + objdir_count, s_ptr->name);
+         arc_n = arc->succ_next;
+         arc->succ_next = arc_p;
        }
-      else
-       source_file_name = s_ptr->name;
-
-      line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno);
-      line_exists = xcalloc (1, s_ptr->maxlineno);
-      if (output_branch_probs)
-       branch_probs = (struct arcdata **)
-         xcalloc (sizeof (struct arcdata *), s_ptr->maxlineno);
-
-      /* There will be a zero at the beginning of the bb info, before the
-        first list of line numbers, so must initialize block_num to 0.  */
-      block_num = 0;
-      this_file = 0;
-      current_graph = 0;
-      {
-       /* Pointer into the bb_data, incremented while scanning the data.  */
-       char *ptr = bb_data;
-       for (count = 0; count < bb_data_size; count++)
-         {
-           long delim;
-
-           __fetch_long (&line_num, ptr, 4);
-           ptr += 4;
-           if (line_num == -1)
-             {
-               /* Marks the beginning of a file name.  Check to see whether
-                  this is the filename we are currently collecting data for.  */
-
-               if (strcmp (s_ptr->name, ptr))
-                 this_file = 0;
-               else
-                 this_file = 1;
-
-               /* Scan past the file name.  */
-               do {
-                 count++;
-                 __fetch_long (&delim, ptr, 4);
-                 ptr += 4;
-               } while (delim != line_num);
-             }
-           else if (line_num == -2)
-             {
-               /* Marks the start of a new function.  Advance to the next
-                  program flow graph.  */
+      fn->blocks[ix].succ = arc_p;
 
-               if (! current_graph)
-                 current_graph = bb_graph_list;
-               else
-                 {
-                   if (block_num == current_graph->num_blocks - 1)
-                     /* Last block falls through to exit.  */
-                     ;
-                   else if (block_num == current_graph->num_blocks - 2)
-                     {
-                       if (output_branch_probs && this_file)
-                         calculate_branch_probs (current_graph, block_num,
-                                                 branch_probs, last_line_num);
-                     }
-                   else
-                     {
-                       fnotice (stderr,
-                                "didn't use all bb entries of graph, function %s\n",
-                                function_name);
-                       fnotice (stderr, "block_num = %ld, num_blocks = %d\n",
-                                block_num, current_graph->num_blocks);
-                     }
+      for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
+          arc_p = arc, arc = arc_n)
+       {
+         arc_n = arc->pred_next;
+         arc->pred_next = arc_p;
+       }
+      fn->blocks[ix].pred = arc_p;
+    }
 
-                   current_graph = current_graph->next;
-                   block_num = 0;
+  if (fn->num_blocks < 2)
+    fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
+            bbg_file_name, fn->name);
+  else
+    {
+      if (fn->blocks[0].num_pred)
+       fnotice (stderr, "%s:'%s' has arcs to entry block\n",
+                bbg_file_name, fn->name);
+      else
+       /* We can't deduce the entry block counts from the lack of
+          predecessors.  */
+       fn->blocks[0].num_pred = ~(unsigned)0;
 
-                   if (output_function_summary && this_file)
-                     function_summary ();
-                 }
+      if (fn->blocks[fn->num_blocks - 1].num_succ)
+       fnotice (stderr, "%s:'%s' has arcs from exit block\n",
+                bbg_file_name, fn->name);
+      else
+       /* Likewise, we can't deduce exit block counts from the lack
+          of its successors.  */
+       fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0;
+    }
 
-               if (output_function_summary)
-                 {
-                   function_source_lines = 0;
-                   function_source_lines_executed = 0;
-                   function_branches = 0;
-                   function_branches_executed = 0;
-                   function_branches_taken = 0;
-                   function_calls = 0;
-                   function_calls_executed = 0;
-                 }
+  /* Propagate the measured counts, this must be done in the same
+     order as the code in profile.c  */
+  for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
+    {
+      block_t const *prev_dst = NULL;
+      int out_of_order = 0;
+      int non_fake_succ = 0;
 
-               /* Save the function name for later use.  */
-               function_name = ptr;
+      for (arc = blk->succ; arc; arc = arc->succ_next)
+       {
+         if (!arc->fake)
+           non_fake_succ++;
 
-               /* Scan past the file name.  */
-               do {
-                 count++;
-                 __fetch_long (&delim, ptr, 4);
-                 ptr += 4;
-               } while (delim != line_num);
-             }
-           else if (line_num == 0)
+         if (!arc->on_tree)
+           {
+             if (count_ptr)
+               arc->count = *count_ptr++;
+             arc->count_valid = 1;
+             blk->num_succ--;
+             arc->dst->num_pred--;
+           }
+         if (prev_dst && prev_dst > arc->dst)
+           out_of_order = 1;
+         prev_dst = arc->dst;
+       }
+      if (non_fake_succ == 1)
+       {
+         /* If there is only one non-fake exit, it is an
+            unconditional branch.  */
+         for (arc = blk->succ; arc; arc = arc->succ_next)
+           if (!arc->fake)
              {
-               /* Marks the end of a block.  */
-
-               if (block_num >= current_graph->num_blocks)
-                 {
-                   fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n",
-                            function_name);
-                   abort ();
-                 }
-
-               if (output_branch_probs && this_file)
-                 calculate_branch_probs (current_graph, block_num,
-                                         branch_probs, last_line_num);
-
-               block_num++;
+               arc->is_unconditional = 1;
+               /* If this block is instrumenting a call, it might be
+                  an artificial block. It is not artificial if it has
+                  a non-fallthrough exit, or the destination of this
+                  arc has more than one entry.  Mark the destination
+                  block as a return site, if none of those conditions
+                  hold.  */
+               if (blk->is_call_site && arc->fall_through
+                   && arc->dst->pred == arc && !arc->pred_next)
+                 arc->dst->is_call_return = 1;
              }
-           else if (this_file)
-             {
-               if (output_function_summary)
-                 {
-                   if (line_exists[line_num] == 0)
-                     function_source_lines++;
-                   if (line_counts[line_num] == 0
-                       && current_graph->bb_graph[block_num].exec_count != 0)
-                     function_source_lines_executed++;
-                 }
+       }
 
-               /* Accumulate execution data for this line number.  */
+      /* Sort the successor arcs into ascending dst order. profile.c
+        normally produces arcs in the right order, but sometimes with
+        one or two out of order.  We're not using a particularly
+        smart sort.  */
+      if (out_of_order)
+       {
+         arc_t *start = blk->succ;
+         unsigned changes = 1;
 
-               line_counts[line_num]
-                 += current_graph->bb_graph[block_num].exec_count;
-               line_exists[line_num] = 1;
-               last_line_num = line_num;
-             }
-         }
-      }
+         while (changes)
+           {
+             arc_t *arc, *arc_p, *arc_n;
 
-      if (output_function_summary && this_file)
-       function_summary ();
+             changes = 0;
+             for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
+               {
+                 if (arc->dst > arc_n->dst)
+                   {
+                     changes = 1;
+                     if (arc_p)
+                       arc_p->succ_next = arc_n;
+                     else
+                       start = arc_n;
+                     arc->succ_next = arc_n->succ_next;
+                     arc_n->succ_next = arc;
+                     arc_p = arc_n;
+                   }
+                 else
+                   {
+                     arc_p = arc;
+                     arc = arc_n;
+                   }
+               }
+           }
+         blk->succ = start;
+       }
 
-      /* Calculate summary test coverage statistics.  */
+      /* Place it on the invalid chain, it will be ignored if that's
+        wrong.  */
+      blk->invalid_chain = 1;
+      blk->chain = invalid_blocks;
+      invalid_blocks = blk;
+    }
 
-      total_source_lines = 0;
-      total_source_lines_executed = 0;
-      total_branches = 0;
-      total_branches_executed = 0;
-      total_branches_taken = 0;
-      total_calls = 0;
-      total_calls_executed = 0;
+  while (invalid_blocks || valid_blocks)
+    {
+      while ((blk = invalid_blocks))
+       {
+         gcov_type total = 0;
+         const arc_t *arc;
+
+         invalid_blocks = blk->chain;
+         blk->invalid_chain = 0;
+         if (!blk->num_succ)
+           for (arc = blk->succ; arc; arc = arc->succ_next)
+             total += arc->count;
+         else if (!blk->num_pred)
+           for (arc = blk->pred; arc; arc = arc->pred_next)
+             total += arc->count;
+         else
+           continue;
 
-      for (count = 1; count < s_ptr->maxlineno; count++)
+         blk->count = total;
+         blk->count_valid = 1;
+         blk->chain = valid_blocks;
+         blk->valid_chain = 1;
+         valid_blocks = blk;
+       }
+      while ((blk = valid_blocks))
        {
-         if (line_exists[count])
+         gcov_type total;
+         arc_t *arc, *inv_arc;
+
+         valid_blocks = blk->chain;
+         blk->valid_chain = 0;
+         if (blk->num_succ == 1)
            {
-             total_source_lines++;
-             if (line_counts[count])
-               total_source_lines_executed++;
+             block_t *dst;
+
+             total = blk->count;
+             inv_arc = NULL;
+             for (arc = blk->succ; arc; arc = arc->succ_next)
+               {
+                 total -= arc->count;
+                 if (!arc->count_valid)
+                   inv_arc = arc;
+               }
+             dst = inv_arc->dst;
+             inv_arc->count_valid = 1;
+             inv_arc->count = total;
+             blk->num_succ--;
+             dst->num_pred--;
+             if (dst->count_valid)
+               {
+                 if (dst->num_pred == 1 && !dst->valid_chain)
+                   {
+                     dst->chain = valid_blocks;
+                     dst->valid_chain = 1;
+                     valid_blocks = dst;
+                   }
+               }
+             else
+               {
+                 if (!dst->num_pred && !dst->invalid_chain)
+                   {
+                     dst->chain = invalid_blocks;
+                     dst->invalid_chain = 1;
+                     invalid_blocks = dst;
+                   }
+               }
            }
-         if (output_branch_probs)
+         if (blk->num_pred == 1)
            {
-             for (a_ptr = branch_probs[count]; a_ptr; a_ptr = a_ptr->next)
+             block_t *src;
+
+             total = blk->count;
+             inv_arc = NULL;
+             for (arc = blk->pred; arc; arc = arc->pred_next)
                {
-                 if (a_ptr->call_insn)
+                 total -= arc->count;
+                 if (!arc->count_valid)
+                   inv_arc = arc;
+               }
+             src = inv_arc->src;
+             inv_arc->count_valid = 1;
+             inv_arc->count = total;
+             blk->num_pred--;
+             src->num_succ--;
+             if (src->count_valid)
+               {
+                 if (src->num_succ == 1 && !src->valid_chain)
                    {
-                     total_calls++;
-                     if (a_ptr->total != 0)
-                       total_calls_executed++;
+                     src->chain = valid_blocks;
+                     src->valid_chain = 1;
+                     valid_blocks = src;
                    }
-                 else
+               }
+             else
+               {
+                 if (!src->num_succ && !src->invalid_chain)
                    {
-                     total_branches++;
-                     if (a_ptr->total != 0)
-                       total_branches_executed++;
-                     if (a_ptr->hits > 0)
-                       total_branches_taken++;
+                     src->chain = invalid_blocks;
+                     src->invalid_chain = 1;
+                     invalid_blocks = src;
                    }
                }
            }
        }
+    }
 
-      if (total_source_lines)
-       fnotice (stdout,
-                "%6.2f%% of %d source lines executed in file %s\n",
-                (((double) total_source_lines_executed / total_source_lines)
-                 * 100), total_source_lines, source_file_name);
-      else
-       fnotice (stdout, "No executable source lines in file %s\n",
-                source_file_name);
+  /* If the graph has been correctly solved, every block will have a
+     valid count.  */
+  for (ix = 0; ix < fn->num_blocks; ix++)
+    if (!fn->blocks[ix].count_valid)
+      {
+       fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
+                bbg_file_name, fn->name);
+       break;
+      }
+}
+
+/* Mark all the blocks only reachable via an incoming catch.  */
 
-      if (output_branch_probs)
+static void
+find_exception_blocks (function_t *fn)
+{
+  unsigned ix;
+  block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);
+
+  /* First mark all blocks as exceptional.  */
+  for (ix = fn->num_blocks; ix--;)
+    fn->blocks[ix].exceptional = 1;
+
+  /* Now mark all the blocks reachable via non-fake edges */
+  queue[0] = fn->blocks;
+  queue[0]->exceptional = 0;
+  for (ix = 1; ix;)
+    {
+      block_t *block = queue[--ix];
+      const arc_t *arc;
+      
+      for (arc = block->succ; arc; arc = arc->succ_next)
+       if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
+         {
+           arc->dst->exceptional = 0;
+           queue[ix++] = arc->dst;
+         }
+    }
+}
+\f
+
+/* Increment totals in COVERAGE according to arc ARC.  */
+
+static void
+add_branch_counts (coverage_t *coverage, const arc_t *arc)
+{
+  if (arc->is_call_non_return)
+    {
+      coverage->calls++;
+      if (arc->src->count)
+       coverage->calls_executed++;
+    }
+  else if (!arc->is_unconditional)
+    {
+      coverage->branches++;
+      if (arc->src->count)
+       coverage->branches_executed++;
+      if (arc->count)
+       coverage->branches_taken++;
+    }
+}
+
+/* Format a HOST_WIDE_INT as either a percent ratio, or absolute
+   count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
+   If DP is zero, no decimal point is printed. Only print 100% when
+   TOP==BOTTOM and only print 0% when TOP=0.  If dp < 0, then simply
+   format TOP.  Return pointer to a static string.  */
+
+static char const *
+format_gcov (gcov_type top, gcov_type bottom, int dp)
+{
+  static char buffer[20];
+
+  if (dp >= 0)
+    {
+      float ratio = bottom ? (float)top / bottom : 0;
+      int ix;
+      unsigned limit = 100;
+      unsigned percent;
+
+      for (ix = dp; ix--; )
+       limit *= 10;
+
+      percent = (unsigned) (ratio * limit + (float)0.5);
+      if (percent <= 0 && top)
+       percent = 1;
+      else if (percent >= limit && top != bottom)
+       percent = limit - 1;
+      ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
+      if (dp)
        {
-         if (total_branches)
+         dp++;
+         do
            {
-             fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n",
-                      (((double) total_branches_executed / total_branches)
-                       * 100), total_branches, source_file_name);
-             fnotice (stdout,
-                   "%6.2f%% of %d branches taken at least once in file %s\n",
-                      (((double) total_branches_taken / total_branches)
-                       * 100), total_branches, source_file_name);
+             buffer[ix+1] = buffer[ix];
+             ix--;
            }
-         else
-           fnotice (stdout, "No branches in file %s\n", source_file_name);
-         if (total_calls)
-           fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n",
-                    (((double) total_calls_executed / total_calls)
-                     * 100), total_calls, source_file_name);
-         else
-           fnotice (stdout, "No calls in file %s\n", source_file_name);
+         while (dp--);
+         buffer[ix + 1] = '.';
        }
+    }
+  else
+    sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
 
-      if (output_gcov_file)
+  return buffer;
+}
+
+/* Summary of execution */
+
+static void
+executed_summary (unsigned lines, unsigned executed)
+{
+  if (lines)
+    fnotice (stdout, "Lines executed:%s of %d\n",
+            format_gcov (executed, lines, 2), lines);
+  else
+    fnotice (stdout, "No executable lines\n");
+}
+  
+/* Output summary info for a function or file.  */
+
+static void
+function_summary (const coverage_t *coverage, const char *title)
+{
+  fnotice (stdout, "%s '%s'\n", title, coverage->name);
+  executed_summary (coverage->lines, coverage->lines_executed);
+
+  if (flag_branches)
+    {
+      if (coverage->branches)
        {
-         /* Now the statistics are ready.  Read in the source file one line
-            at a time, and output that line to the gcov file preceded by
-            its execution count if non zero.  */
+         fnotice (stdout, "Branches executed:%s of %d\n",
+                  format_gcov (coverage->branches_executed,
+                               coverage->branches, 2),
+                  coverage->branches);
+         fnotice (stdout, "Taken at least once:%s of %d\n",
+                  format_gcov (coverage->branches_taken,
+                               coverage->branches, 2),
+                  coverage->branches);
+       }
+      else
+       fnotice (stdout, "No branches\n");
+      if (coverage->calls)
+       fnotice (stdout, "Calls executed:%s of %d\n",
+                format_gcov (coverage->calls_executed, coverage->calls, 2),
+                coverage->calls);
+      else
+       fnotice (stdout, "No calls\n");
+    }
+}
+
+/* Canonicalize the filename NAME by canonicalizing directory
+   separators, eliding . components and resolving .. components
+   appropriately.  Always returns a unique string.  */
 
-         source_file = fopen (source_file_name, "r");
-         if (source_file == NULL)
+static char *
+canonicalize_name (const char *name)
+{
+  /* The canonical name cannot be longer than the incoming name.  */
+  char *result = XNEWVEC (char, strlen (name) + 1);
+  const char *base = name, *probe;
+  char *ptr = result;
+  char *dd_base;
+  int slash = 0;
+
+#if HAVE_DOS_BASED_FILE_SYSTEM
+  if (base[0] && base[1] == ':')
+    {
+      result[0] = base[0];
+      result[1] = ':';
+      base += 2;
+      ptr += 2;
+    }
+#endif
+  for (dd_base = ptr; *base; base = probe)
+    {
+      size_t len;
+      
+      for (probe = base; *probe; probe++)
+       if (IS_DIR_SEPARATOR (*probe))
+         break;
+
+      len = probe - base;
+      if (len == 1 && base[0] == '.')
+       /* Elide a '.' directory */
+       ;
+      else if (len == 2 && base[0] == '.' && base[1] == '.')
+       {
+         /* '..', we can only elide it and the previous directory, if
+            we're not a symlink.  */
+         struct stat ATTRIBUTE_UNUSED buf;
+
+         *ptr = 0;
+         if (dd_base == ptr
+#if defined (S_ISLNK)
+             /* S_ISLNK is not POSIX.1-1996.  */
+             || stat (result, &buf) || S_ISLNK (buf.st_mode)
+#endif
+             )
            {
-             fnotice (stderr, "Could not open source file %s.\n",
-                      source_file_name);
-             free (line_counts);
-             free (line_exists);
-             continue;
+             /* Cannot elide, or unreadable or a symlink.  */
+             dd_base = ptr + 2 + slash;
+             goto regular;
            }
+         while (ptr != dd_base && *ptr != '/')
+           ptr--;
+         slash = ptr != result;
+       }
+      else
+       {
+       regular:
+         /* Regular pathname component.  */
+         if (slash)
+           *ptr++ = '/';
+         memcpy (ptr, base, len);
+         ptr += len;
+         slash = 1;
+       }
 
-         count = strlen (source_file_name);
-         cptr = strrchr (s_ptr->name, '/');
-         if (cptr)
-           cptr = cptr + 1;
-         else
-           cptr = s_ptr->name;
-         if (output_long_names && strcmp (cptr, input_file_name))
-           {
-             gcov_file_name = xmalloc (count + 7 + strlen (input_file_name));
+      for (; IS_DIR_SEPARATOR (*probe); probe++)
+       continue;
+    }
+  *ptr = 0;
 
-             cptr = strrchr (input_file_name, '/');
-             if (cptr)
-               strcpy (gcov_file_name, cptr + 1);
-             else
-               strcpy (gcov_file_name, input_file_name);
+  return result;
+}
 
-             strcat (gcov_file_name, ".");
+/* Generate an output file name. INPUT_NAME is the canonicalized main
+   input file and SRC_NAME is the canonicalized file name.
+   LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation.  With
+   long_output_names we prepend the processed name of the input file
+   to each output name (except when the current source file is the
+   input file, so you don't get a double concatenation). The two
+   components are separated by '##'.  With preserve_paths we create a
+   filename from all path components of the source file, replacing '/'
+   with '#', and .. with '^', without it we simply take the basename
+   component.  (Remember, the canonicalized name will already have
+   elided '.' components and converted \\ separators.)  */
+
+static char *
+make_gcov_file_name (const char *input_name, const char *src_name)
+{
+  char *ptr;
+  char *result;
 
-             cptr = strrchr (source_file_name, '/');
-             if (cptr)
-               strcat (gcov_file_name, cptr + 1);
-             else
-               strcat (gcov_file_name, source_file_name);
-           }
+  if (flag_long_names && input_name && strcmp (src_name, input_name))
+    {
+      /* Generate the input filename part.  */
+      result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
+  
+      ptr = result;
+      ptr = mangle_name (input_name, ptr);
+      ptr[0] = ptr[1] = '#';
+      ptr += 2;
+    }
+  else
+    {
+      result = XNEWVEC (char, strlen (src_name) + 10);
+      ptr = result;
+    }
+
+  ptr = mangle_name (src_name, ptr);
+  strcpy (ptr, ".gcov");
+  
+  return result;
+}
+
+static char *
+mangle_name (char const *base, char *ptr)
+{
+  size_t len;
+  
+  /* Generate the source filename part.  */
+  if (!flag_preserve_paths)
+    {
+      base = lbasename (base);
+      len = strlen (base);
+      memcpy (ptr, base, len);
+      ptr += len;
+    }
+  else
+    {
+      /* Convert '/' to '#', convert '..' to '^',
+        convert ':' to '~' on DOS based file system.  */
+      const char *probe;
+
+#if HAVE_DOS_BASED_FILE_SYSTEM
+      if (base[0] && base[1] == ':')
+       {
+         ptr[0] = base[0];
+         ptr[1] = '~';
+         ptr += 2;
+         base += 2;
+       }
+#endif
+      for (; *base; base = probe)
+       {
+         size_t len;
+
+         for (probe = base; *probe; probe++)
+           if (*probe == '/')
+             break;
+         len = probe - base;
+         if (len == 2 && base[0] == '.' && base[1] == '.')
+           *ptr++ = '^';
          else
            {
-             gcov_file_name = xmalloc (count + 6);
-             cptr = strrchr (source_file_name, '/');
-             if (cptr)
-               strcpy (gcov_file_name, cptr + 1);
-             else
-               strcpy (gcov_file_name, source_file_name);
+             memcpy (ptr, base, len);
+             ptr += len;
+           }
+         if (*probe)
+           {
+             *ptr++ = '#';
+             probe++;
            }
+       }
+    }
+  
+  return ptr;
+}
+
+/* Scan through the bb_data for each line in the block, increment
+   the line number execution count indicated by the execution count of
+   the appropriate basic block.  */
+
+static void
+add_line_counts (coverage_t *coverage, function_t *fn)
+{
+  unsigned ix;
+  line_t *line = NULL; /* This is propagated from one iteration to the
+                         next.  */
+
+  /* Scan each basic block.  */
+  for (ix = 0; ix != fn->num_blocks; ix++)
+    {
+      block_t *block = &fn->blocks[ix];
+      unsigned *encoding;
+      const source_t *src = NULL;
+      unsigned jx;
+
+      if (block->count && ix && ix + 1 != fn->num_blocks)
+       fn->blocks_executed++;
+      for (jx = 0, encoding = block->u.line.encoding;
+          jx != block->u.line.num; jx++, encoding++)
+       if (!*encoding)
+         {
+           src = &sources[*++encoding];
+           jx++;
+         }
+       else
+         {
+           line = &src->lines[*encoding];
 
-         /* Don't strip off the ending for compatibility with tcov, since
-            this results in confusion if there is more than one file with
-            the same basename, e.g. tmp.c and tmp.h.  */
-         strcat (gcov_file_name, ".gcov");
+           if (coverage)
+             {
+               if (!line->exists)
+                 coverage->lines++;
+               if (!line->count && block->count)
+                 coverage->lines_executed++;
+             }
+           line->exists = 1;
+           if (!block->exceptional)
+             line->unexceptional = 1;
+           line->count += block->count;
+         }
+      free (block->u.line.encoding);
+      block->u.cycle.arc = NULL;
+      block->u.cycle.ident = ~0U;
 
-         gcov_file = fopen (gcov_file_name, "w");
+      if (!ix || ix + 1 == fn->num_blocks)
+       /* Entry or exit block */;
+      else if (flag_all_blocks)
+       {
+         line_t *block_line = line;
 
-         if (gcov_file == NULL)
+         if (!block_line)
+           block_line = &sources[fn->src].lines[fn->line];
+
+         block->chain = block_line->u.blocks;
+         block_line->u.blocks = block;
+       }
+      else if (flag_branches)
+       {
+         arc_t *arc;
+
+         for (arc = block->succ; arc; arc = arc->succ_next)
            {
-             fnotice (stderr, "Could not open output file %s.\n",
-                      gcov_file_name);
-             fclose (source_file);
-             free (line_counts);
-             free (line_exists);
-             continue;
+             arc->line_next = line->u.branches;
+             line->u.branches = arc;
+             if (coverage && !arc->is_unconditional)
+               add_branch_counts (coverage, arc);
            }
+       }
+    }
+  if (!line)
+    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
+}
+
+/* Accumulate the line counts of a file.  */
+
+static void
+accumulate_line_counts (source_t *src)
+{
+  line_t *line;
+  function_t *fn, *fn_p, *fn_n;
+  unsigned ix;
 
-         fnotice (stdout, "Creating %s.\n", gcov_file_name);
+  /* Reverse the function order.  */
+  for (fn = src->functions, fn_p = NULL; fn;
+       fn_p = fn, fn = fn_n)
+    {
+      fn_n = fn->line_next;
+      fn->line_next = fn_p;
+    }
+  src->functions = fn_p;
+
+  for (ix = src->num_lines, line = src->lines; ix--; line++)
+    {
+      if (!flag_all_blocks)
+       {
+         arc_t *arc, *arc_p, *arc_n;
 
-         for (count = 1; count < s_ptr->maxlineno; count++)
+         /* Total and reverse the branch information.  */
+         for (arc = line->u.branches, arc_p = NULL; arc;
+              arc_p = arc, arc = arc_n)
            {
-             char *retval;
-             int len;
-
-             retval = fgets (string, STRING_SIZE, source_file);
-
-             /* For lines which don't exist in the .bb file, print nothing
-                before the source line.  For lines which exist but were never
-                executed, print ###### before the source line.  Otherwise,
-                print the execution count before the source line.  */
-             /* There are 16 spaces of indentation added before the source
-                line so that tabs won't be messed up.  */
-             if (line_exists[count])
-               {
-                 if (line_counts[count])
-                   {
-                     char c[20];
-                     sprintf (c, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)line_counts[count]);
-                     fprintf (gcov_file, "%12s    %s", c,
-                              string);
-                   }
-                 else
-                   fprintf (gcov_file, "      ######    %s", string);
-               }
-             else
-               fprintf (gcov_file, "\t\t%s", string);
+             arc_n = arc->line_next;
+             arc->line_next = arc_p;
 
-             /* In case the source file line is larger than our buffer, keep
-                reading and outputting lines until we get a newline.  */
-             len = strlen (string);
-             while ((len == 0 || string[strlen (string) - 1] != '\n')
-                    && retval != NULL)
+             add_branch_counts (&src->coverage, arc);
+           }
+         line->u.branches = arc_p;
+       }
+      else if (line->u.blocks)
+       {
+         /* The user expects the line count to be the number of times
+            a line has been executed. Simply summing the block count
+            will give an artificially high number.  The Right Thing
+            is to sum the entry counts to the graph of blocks on this
+            line, then find the elementary cycles of the local graph
+            and add the transition counts of those cycles.  */
+         block_t *block, *block_p, *block_n;
+         gcov_type count = 0;
+
+         /* Reverse the block information.  */
+         for (block = line->u.blocks, block_p = NULL; block;
+              block_p = block, block = block_n)
+           {
+             block_n = block->chain;
+             block->chain = block_p;
+             block->u.cycle.ident = ix;
+           }
+         line->u.blocks = block_p;
+
+         /* Sum the entry arcs.  */
+         for (block = line->u.blocks; block; block = block->chain)
+           {
+             arc_t *arc;
+
+             for (arc = block->pred; arc; arc = arc->pred_next)
                {
-                 retval = fgets (string, STRING_SIZE, source_file);
-                 fputs (string, gcov_file);
+                 if (arc->src->u.cycle.ident != ix)
+                   count += arc->count;
+                 if (flag_branches)
+                   add_branch_counts (&src->coverage, arc);
                }
 
-             if (output_branch_probs)
+             /* Initialize the cs_count.  */
+             for (arc = block->succ; arc; arc = arc->succ_next)
+               arc->cs_count = arc->count;
+           }
+
+         /* Find the loops. This uses the algorithm described in
+            Tiernan 'An Efficient Search Algorithm to Find the
+            Elementary Circuits of a Graph', CACM Dec 1970. We hold
+            the P array by having each block point to the arc that
+            connects to the previous block. The H array is implicitly
+            held because of the arc ordering, and the block's
+            previous arc pointer.
+
+            Although the algorithm is O(N^3) for highly connected
+            graphs, at worst we'll have O(N^2), as most blocks have
+            only one or two exits. Most graphs will be small.
+
+            For each loop we find, locate the arc with the smallest
+            transition count, and add that to the cumulative
+            count.  Decrease flow over the cycle and remove the arc
+            from consideration.  */
+         for (block = line->u.blocks; block; block = block->chain)
+           {
+             block_t *head = block;
+             arc_t *arc;
+
+           next_vertex:;
+             arc = head->succ;
+           current_vertex:;
+             while (arc)
                {
-                 for (i = 0, a_ptr = branch_probs[count]; a_ptr;
-                      a_ptr = a_ptr->next, i++)
+                 block_t *dst = arc->dst;
+                 if (/* Already used that arc.  */
+                     arc->cycle
+                     /* Not to same graph, or before first vertex.  */
+                     || dst->u.cycle.ident != ix
+                     /* Already in path.  */
+                     || dst->u.cycle.arc)
                    {
-                     if (a_ptr->call_insn)
-                       {
-                         if (a_ptr->total == 0)
-                           fnotice (gcov_file, "call %d never executed\n", i);
-                           else
-                             {
-                               if (output_branch_counts)
-                                 {
-                                   char c[20];
-                                   sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
-                                            a_ptr->total - a_ptr->hits);
-                                   fnotice (gcov_file,
-                                            "call %d returns = %s\n", i, c);
-                                 }
-                               else
-                                 {
-                                   char c[20];
-                                   sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
-                                            100 - ((a_ptr->hits * 100)
-                                                   + (a_ptr->total >> 1))
-                                            / a_ptr->total);
-                                   fnotice (gcov_file,
-                                            "call %d returns = %s%%\n", i, c);
-                                 }
-                             }
-                       }
-                     else
+                     arc = arc->succ_next;
+                     continue;
+                   }
+
+                 if (dst == block)
+                   {
+                     /* Found a closing arc.  */
+                     gcov_type cycle_count = arc->cs_count;
+                     arc_t *cycle_arc = arc;
+                     arc_t *probe_arc;
+
+                     /* Locate the smallest arc count of the loop.  */
+                     for (dst = head; (probe_arc = dst->u.cycle.arc);
+                          dst = probe_arc->src)
+                       if (cycle_count > probe_arc->cs_count)
+                         {
+                           cycle_count = probe_arc->cs_count;
+                           cycle_arc = probe_arc;
+                         }
+
+                     count += cycle_count;
+                     cycle_arc->cycle = 1;
+
+                     /* Remove the flow from the cycle.  */
+                     arc->cs_count -= cycle_count;
+                     for (dst = head; (probe_arc = dst->u.cycle.arc);
+                          dst = probe_arc->src)
+                       probe_arc->cs_count -= cycle_count;
+
+                     /* Unwind to the cyclic arc.  */
+                     while (head != cycle_arc->src)
                        {
-                         if (a_ptr->total == 0)
-                           fnotice (gcov_file, "branch %d never executed\n",
-                                    i);
-                         else
-                           {
-                             if (output_branch_counts)
-                               {
-                                 char c[20];
-                                 sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
-                                          a_ptr->hits);
-                                 fnotice (gcov_file,
-                                          "branch %d taken = %s\n", i, c);
-                               }
-                             else
-                               {
-                                 char c[20];
-                                 sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
-                                          ((a_ptr->hits * 100)
-                                           + (a_ptr->total >> 1))
-                                          / a_ptr->total);
-                                fnotice (gcov_file,
-                                         "branch %d taken = %s%%\n", i, c);
-                               }
-                           }
+                         arc = head->u.cycle.arc;
+                         head->u.cycle.arc = NULL;
+                         head = arc->src;
                        }
-                  }
-             }
+                     /* Move on.  */
+                     arc = arc->succ_next;
+                     continue;
+                   }
 
-             /* Gracefully handle errors while reading the source file.  */
-             if (retval == NULL)
+                 /* Add new block to chain.  */
+                 dst->u.cycle.arc = arc;
+                 head = dst;
+                 goto next_vertex;
+               }
+             /* We could not add another vertex to the path. Remove
+                the last vertex from the list.  */
+             arc = head->u.cycle.arc;
+             if (arc)
                {
-                 fnotice (stderr,
-                          "Unexpected EOF while reading source file %s.\n",
-                          source_file_name);
-                 break;
+                 /* It was not the first vertex. Move onto next arc.  */
+                 head->u.cycle.arc = NULL;
+                 head = arc->src;
+                 arc = arc->succ_next;
+                 goto current_vertex;
                }
+             /* Mark this block as unusable.  */
+             block->u.cycle.ident = ~0U;
            }
 
-         /* Handle all remaining source lines.  There may be lines
-            after the last line of code.  */
+         line->count = count;
+       }
 
-         {
-           char *retval = fgets (string, STRING_SIZE, source_file);
-           while (retval != NULL)
-             {
-               int len;
+      if (line->exists)
+       {
+         src->coverage.lines++;
+         if (line->count)
+           src->coverage.lines_executed++;
+       }
+    }
+}
 
-               fprintf (gcov_file, "\t\t%s", string);
+/* Output information about ARC number IX.  Returns nonzero if
+   anything is output.  */
 
-               /* In case the source file line is larger than our buffer, keep
-                  reading and outputting lines until we get a newline.  */
-               len = strlen (string);
-               while ((len == 0 || string[strlen (string) - 1] != '\n')
-                      && retval != NULL)
-                 {
-                   retval = fgets (string, STRING_SIZE, source_file);
-                   fputs (string, gcov_file);
-                 }
+static int
+output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
+{
+  if (arc->is_call_non_return)
+    {
+      if (arc->src->count)
+       {
+         fnotice (gcov_file, "call   %2d returned %s\n", ix,
+                  format_gcov (arc->src->count - arc->count,
+                               arc->src->count, -flag_counts));
+       }
+      else
+       fnotice (gcov_file, "call   %2d never executed\n", ix);
+    }
+  else if (!arc->is_unconditional)
+    {
+      if (arc->src->count)
+       fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
+                format_gcov (arc->count, arc->src->count, -flag_counts),
+                arc->fall_through ? " (fallthrough)"
+                : arc->is_throw ? " (throw)" : "");
+      else
+       fnotice (gcov_file, "branch %2d never executed\n", ix);
+    }
+  else if (flag_unconditional && !arc->dst->is_call_return)
+    {
+      if (arc->src->count)
+       fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
+                format_gcov (arc->count, arc->src->count, -flag_counts));
+      else
+       fnotice (gcov_file, "unconditional %2d never executed\n", ix);
+    }
+  else
+    return 0;
+  return 1;
 
-               retval = fgets (string, STRING_SIZE, source_file);
-             }
-         }
+}
 
-         fclose (source_file);
-         fclose (gcov_file);
+static const char *
+read_line (FILE *file)
+{
+  static char *string;
+  static size_t string_len;
+  size_t pos = 0;
+  char *ptr;
+
+  if (!string_len)
+    {
+      string_len = 200;
+      string = XNEWVEC (char, string_len);
+    }
+
+  while ((ptr = fgets (string + pos, string_len - pos, file)))
+    {
+      size_t len = strlen (string + pos);
+
+      if (string[pos + len - 1] == '\n')
+       {
+         string[pos + len - 1] = 0;
+         return string;
+       }
+      pos += len;
+      ptr = XNEWVEC (char, string_len * 2);
+      if (ptr)
+       {
+         memcpy (ptr, string, pos);
+         string = ptr;
+         string_len += 2;
+       }
+      else
+       pos = 0;
+    }
+      
+  return pos ? string : NULL;
+}
+
+/* Read in the source file one line at a time, and output that line to
+   the gcov file preceded by its execution count and other
+   information.  */
+
+static void
+output_lines (FILE *gcov_file, const source_t *src)
+{
+  FILE *source_file;
+  unsigned line_num;   /* current line number.  */
+  const line_t *line;           /* current line info ptr.  */
+  const char *retval = "";     /* status of source file reading.  */
+  function_t *fn = NULL;
+
+  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
+  if (!multiple_files)
+    {
+      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
+      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
+              no_data_file ? "-" : da_file_name);
+      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
+    }
+  fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
+
+  source_file = fopen (src->name, "r");
+  if (!source_file)
+    {
+      fnotice (stderr, "Cannot open source file %s\n", src->name);
+      retval = NULL;
+    }
+  else if (src->file_time == 0)
+    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
+
+  if (flag_branches)
+    fn = src->functions;
+
+  for (line_num = 1, line = &src->lines[line_num];
+       line_num < src->num_lines; line_num++, line++)
+    {
+      for (; fn && fn->line == line_num; fn = fn->line_next)
+       {
+         arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
+         gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
+
+         for (; arc; arc = arc->pred_next)
+           if (arc->fake)
+             return_count -= arc->count;
+
+         fprintf (gcov_file, "function %s", fn->name);
+         fprintf (gcov_file, " called %s",
+                  format_gcov (fn->blocks[0].count, 0, -1));
+         fprintf (gcov_file, " returned %s",
+                  format_gcov (return_count, fn->blocks[0].count, 0));
+         fprintf (gcov_file, " blocks executed %s",
+                  format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
+         fprintf (gcov_file, "\n");
        }
 
-      free (line_counts);
-      free (line_exists);
+      if (retval)
+       retval = read_line (source_file);
+
+      /* For lines which don't exist in the .bb file, print '-' before
+        the source line.  For lines which exist but were never
+        executed, print '#####' or '=====' before the source line.
+        Otherwise, print the execution count before the source line.
+        There are 16 spaces of indentation added before the source
+        line so that tabs won't be messed up.  */
+      fprintf (gcov_file, "%9s:%5u:%s\n",
+              !line->exists ? "-" : line->count
+              ? format_gcov (line->count, 0, -1)
+              : line->unexceptional ? "#####" : "=====", line_num,
+              retval ? retval : "/*EOF*/");
+
+      if (flag_all_blocks)
+       {
+         block_t *block;
+         arc_t *arc;
+         int ix, jx;
+
+         for (ix = jx = 0, block = line->u.blocks; block;
+              block = block->chain)
+           {
+             if (!block->is_call_return)
+               fprintf (gcov_file, "%9s:%5u-block %2d\n",
+                        !line->exists ? "-" : block->count
+                        ? format_gcov (block->count, 0, -1)
+                        : block->exceptional ? "%%%%%" : "$$$$$",
+                        line_num, ix++);
+             if (flag_branches)
+               for (arc = block->succ; arc; arc = arc->succ_next)
+                 jx += output_branch_count (gcov_file, jx, arc);
+           }
+       }
+      else if (flag_branches)
+       {
+         int ix;
+         arc_t *arc;
+
+         for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
+           ix += output_branch_count (gcov_file, ix, arc);
+       }
     }
+
+  /* Handle all remaining source lines.  There may be lines after the
+     last line of code.  */
+  if (retval)
+    {
+      for (; (retval = read_line (source_file)); line_num++)
+       fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
+    }
+
+  if (source_file)
+    fclose (source_file);
 }