OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [tags/] [gnu-dev/] [fsf-gcc-snapshot-1-mar-12/] [or1k-gcc/] [gcc/] [gcov.c] - Diff between revs 684 and 783

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 684 Rev 783
/* Gcov.c: prepend line execution counts and branch probabilities to a
/* Gcov.c: prepend line execution counts and branch probabilities to a
   source file.
   source file.
   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
   Contributed by James E. Wilson of Cygnus Support.
   Contributed by James E. Wilson of Cygnus Support.
   Mangled by Bob Manson of Cygnus Support.
   Mangled by Bob Manson of Cygnus Support.
   Mangled further by Nathan Sidwell <nathan@codesourcery.com>
   Mangled further by Nathan Sidwell <nathan@codesourcery.com>
 
 
Gcov is free software; you can redistribute it and/or modify
Gcov is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
the Free Software Foundation; either version 3, or (at your option)
any later version.
any later version.
 
 
Gcov is distributed in the hope that it will be useful,
Gcov is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
GNU General Public License for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with Gcov; see the file COPYING3.  If not see
along with Gcov; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
<http://www.gnu.org/licenses/>.  */
 
 
/* ??? Print a list of the ten blocks with the highest execution counts,
/* ??? Print a list of the ten blocks with the highest execution counts,
   and list the line numbers corresponding to those blocks.  Also, perhaps
   and list the line numbers corresponding to those blocks.  Also, perhaps
   list the line numbers with the highest execution counts, only printing
   list the line numbers with the highest execution counts, only printing
   the first if there are several which are all listed in the same block.  */
   the first if there are several which are all listed in the same block.  */
 
 
/* ??? Should have an option to print the number of basic blocks, and the
/* ??? Should have an option to print the number of basic blocks, and the
   percent of them that are covered.  */
   percent of them that are covered.  */
 
 
/* Need an option to show individual block counts, and show
/* Need an option to show individual block counts, and show
   probabilities of fall through arcs.  */
   probabilities of fall through arcs.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tm.h"
#include "tm.h"
#include "intl.h"
#include "intl.h"
#include "diagnostic.h"
#include "diagnostic.h"
#include "version.h"
#include "version.h"
 
 
#include <getopt.h>
#include <getopt.h>
 
 
#define IN_GCOV 1
#define IN_GCOV 1
#include "gcov-io.h"
#include "gcov-io.h"
#include "gcov-io.c"
#include "gcov-io.c"
 
 
/* The gcno file is generated by -ftest-coverage option. The gcda file is
/* The gcno file is generated by -ftest-coverage option. The gcda file is
   generated by a program compiled with -fprofile-arcs. Their formats
   generated by a program compiled with -fprofile-arcs. Their formats
   are documented in gcov-io.h.  */
   are documented in gcov-io.h.  */
 
 
/* The functions in this file for creating and solution program flow graphs
/* 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
   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
   some places we make use of the knowledge of how profile.c works to
   select particular algorithms here.  */
   select particular algorithms here.  */
 
 
/* The code validates that the profile information read in corresponds
/* The code validates that the profile information read in corresponds
   to the code currently being compiled.  Rather than checking for
   to the code currently being compiled.  Rather than checking for
   identical files, the code below computes a checksum on the CFG
   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
   (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
   the CFG checksum in the gcda file match the CFG checksum for the
   code currently being compiled, the profile data will be used.  */
   code currently being compiled, the profile data will be used.  */
 
 
/* This is the size of the buffer used to read in source file lines.  */
/* This is the size of the buffer used to read in source file lines.  */
 
 
struct function_info;
struct function_info;
struct block_info;
struct block_info;
struct source_info;
struct source_info;
 
 
/* Describes an arc between two basic blocks.  */
/* Describes an arc between two basic blocks.  */
 
 
typedef struct arc_info
typedef struct arc_info
{
{
  /* source and destination blocks.  */
  /* source and destination blocks.  */
  struct block_info *src;
  struct block_info *src;
  struct block_info *dst;
  struct block_info *dst;
 
 
  /* transition counts.  */
  /* transition counts.  */
  gcov_type count;
  gcov_type count;
  /* used in cycle search, so that we do not clobber original counts.  */
  /* used in cycle search, so that we do not clobber original counts.  */
  gcov_type cs_count;
  gcov_type cs_count;
 
 
  unsigned int count_valid : 1;
  unsigned int count_valid : 1;
  unsigned int on_tree : 1;
  unsigned int on_tree : 1;
  unsigned int fake : 1;
  unsigned int fake : 1;
  unsigned int fall_through : 1;
  unsigned int fall_through : 1;
 
 
  /* Arc to a catch handler.  */
  /* Arc to a catch handler.  */
  unsigned int is_throw : 1;
  unsigned int is_throw : 1;
 
 
  /* Arc is for a function that abnormally returns.  */
  /* Arc is for a function that abnormally returns.  */
  unsigned int is_call_non_return : 1;
  unsigned int is_call_non_return : 1;
 
 
  /* Arc is for catch/setjmp.  */
  /* Arc is for catch/setjmp.  */
  unsigned int is_nonlocal_return : 1;
  unsigned int is_nonlocal_return : 1;
 
 
  /* Is an unconditional branch.  */
  /* Is an unconditional branch.  */
  unsigned int is_unconditional : 1;
  unsigned int is_unconditional : 1;
 
 
  /* Loop making arc.  */
  /* Loop making arc.  */
  unsigned int cycle : 1;
  unsigned int cycle : 1;
 
 
  /* Next branch on line.  */
  /* Next branch on line.  */
  struct arc_info *line_next;
  struct arc_info *line_next;
 
 
  /* Links to next arc on src and dst lists.  */
  /* Links to next arc on src and dst lists.  */
  struct arc_info *succ_next;
  struct arc_info *succ_next;
  struct arc_info *pred_next;
  struct arc_info *pred_next;
} arc_t;
} arc_t;
 
 
/* Describes a basic block. Contains lists of arcs to successor and
/* Describes a basic block. Contains lists of arcs to successor and
   predecessor blocks.  */
   predecessor blocks.  */
 
 
typedef struct block_info
typedef struct block_info
{
{
  /* Chain of exit and entry arcs.  */
  /* Chain of exit and entry arcs.  */
  arc_t *succ;
  arc_t *succ;
  arc_t *pred;
  arc_t *pred;
 
 
  /* Number of unprocessed exit and entry arcs.  */
  /* Number of unprocessed exit and entry arcs.  */
  gcov_type num_succ;
  gcov_type num_succ;
  gcov_type num_pred;
  gcov_type num_pred;
 
 
  /* Block execution count.  */
  /* Block execution count.  */
  gcov_type count;
  gcov_type count;
  unsigned flags : 12;
  unsigned flags : 12;
  unsigned count_valid : 1;
  unsigned count_valid : 1;
  unsigned valid_chain : 1;
  unsigned valid_chain : 1;
  unsigned invalid_chain : 1;
  unsigned invalid_chain : 1;
  unsigned exceptional : 1;
  unsigned exceptional : 1;
 
 
  /* Block is a call instrumenting site.  */
  /* Block is a call instrumenting site.  */
  unsigned is_call_site : 1; /* Does the call.  */
  unsigned is_call_site : 1; /* Does the call.  */
  unsigned is_call_return : 1; /* Is the return.  */
  unsigned is_call_return : 1; /* Is the return.  */
 
 
  /* Block is a landing pad for longjmp or throw.  */
  /* Block is a landing pad for longjmp or throw.  */
  unsigned is_nonlocal_return : 1;
  unsigned is_nonlocal_return : 1;
 
 
  union
  union
  {
  {
    struct
    struct
    {
    {
     /* Array of line numbers and source files. source files are
     /* Array of line numbers and source files. source files are
        introduced by a linenumber of zero, the next 'line number' is
        introduced by a linenumber of zero, the next 'line number' is
        the number of the source file.  Always starts with a source
        the number of the source file.  Always starts with a source
        file.  */
        file.  */
      unsigned *encoding;
      unsigned *encoding;
      unsigned num;
      unsigned num;
    } line; /* Valid until blocks are linked onto lines */
    } line; /* Valid until blocks are linked onto lines */
    struct
    struct
    {
    {
      /* Single line graph cycle workspace.  Used for all-blocks
      /* Single line graph cycle workspace.  Used for all-blocks
         mode.  */
         mode.  */
      arc_t *arc;
      arc_t *arc;
      unsigned ident;
      unsigned ident;
    } cycle; /* Used in all-blocks mode, after blocks are linked onto
    } cycle; /* Used in all-blocks mode, after blocks are linked onto
               lines.  */
               lines.  */
  } u;
  } u;
 
 
  /* Temporary chain for solving graph, and for chaining blocks on one
  /* Temporary chain for solving graph, and for chaining blocks on one
     line.  */
     line.  */
  struct block_info *chain;
  struct block_info *chain;
 
 
} block_t;
} block_t;
 
 
/* Describes a single function. Contains an array of basic blocks.  */
/* Describes a single function. Contains an array of basic blocks.  */
 
 
typedef struct function_info
typedef struct function_info
{
{
  /* Name of function.  */
  /* Name of function.  */
  char *name;
  char *name;
  unsigned ident;
  unsigned ident;
  unsigned lineno_checksum;
  unsigned lineno_checksum;
  unsigned cfg_checksum;
  unsigned cfg_checksum;
 
 
  /* The graph contains at least one fake incoming edge.  */
  /* The graph contains at least one fake incoming edge.  */
  unsigned has_catch : 1;
  unsigned has_catch : 1;
 
 
  /* Array of basic blocks.  */
  /* Array of basic blocks.  */
  block_t *blocks;
  block_t *blocks;
  unsigned num_blocks;
  unsigned num_blocks;
  unsigned blocks_executed;
  unsigned blocks_executed;
 
 
  /* Raw arc coverage counts.  */
  /* Raw arc coverage counts.  */
  gcov_type *counts;
  gcov_type *counts;
  unsigned num_counts;
  unsigned num_counts;
 
 
  /* First line number & file.  */
  /* First line number & file.  */
  unsigned line;
  unsigned line;
  unsigned src;
  unsigned src;
 
 
  /* Next function in same source file.  */
  /* Next function in same source file.  */
  struct function_info *line_next;
  struct function_info *line_next;
 
 
  /* Next function.  */
  /* Next function.  */
  struct function_info *next;
  struct function_info *next;
} function_t;
} function_t;
 
 
/* Describes coverage of a file or function.  */
/* Describes coverage of a file or function.  */
 
 
typedef struct coverage_info
typedef struct coverage_info
{
{
  int lines;
  int lines;
  int lines_executed;
  int lines_executed;
 
 
  int branches;
  int branches;
  int branches_executed;
  int branches_executed;
  int branches_taken;
  int branches_taken;
 
 
  int calls;
  int calls;
  int calls_executed;
  int calls_executed;
 
 
  char *name;
  char *name;
} coverage_t;
} coverage_t;
 
 
/* Describes a single line of source. Contains a chain of basic blocks
/* Describes a single line of source. Contains a chain of basic blocks
   with code on it.  */
   with code on it.  */
 
 
typedef struct line_info
typedef struct line_info
{
{
  gcov_type count;         /* execution count */
  gcov_type count;         /* execution count */
  union
  union
  {
  {
    arc_t *branches;       /* branches from blocks that end on this
    arc_t *branches;       /* branches from blocks that end on this
                              line. Used for branch-counts when not
                              line. Used for branch-counts when not
                              all-blocks mode.  */
                              all-blocks mode.  */
    block_t *blocks;       /* blocks which start on this line.  Used
    block_t *blocks;       /* blocks which start on this line.  Used
                              in all-blocks mode.  */
                              in all-blocks mode.  */
  } u;
  } u;
  unsigned exists : 1;
  unsigned exists : 1;
  unsigned unexceptional : 1;
  unsigned unexceptional : 1;
} line_t;
} line_t;
 
 
/* Describes a file mentioned in the block graph.  Contains an array
/* Describes a file mentioned in the block graph.  Contains an array
   of line info.  */
   of line info.  */
 
 
typedef struct source_info
typedef struct source_info
{
{
  /* Canonical name of source file.  */
  /* Canonical name of source file.  */
  char *name;
  char *name;
  time_t file_time;
  time_t file_time;
 
 
  /* Array of line information.  */
  /* Array of line information.  */
  line_t *lines;
  line_t *lines;
  unsigned num_lines;
  unsigned num_lines;
 
 
  coverage_t coverage;
  coverage_t coverage;
 
 
  /* Functions in this source file.  These are in ascending line
  /* Functions in this source file.  These are in ascending line
     number order.  */
     number order.  */
  function_t *functions;
  function_t *functions;
} source_t;
} source_t;
 
 
typedef struct name_map
typedef struct name_map
{
{
  char *name;  /* Source file name */
  char *name;  /* Source file name */
  unsigned src;  /* Source file */
  unsigned src;  /* Source file */
} name_map_t;
} name_map_t;
 
 
/* Holds a list of function basic block graphs.  */
/* Holds a list of function basic block graphs.  */
 
 
static function_t *functions;
static function_t *functions;
static function_t **fn_end = &functions;
static function_t **fn_end = &functions;
 
 
static source_t *sources;   /* Array of source files  */
static source_t *sources;   /* Array of source files  */
static unsigned n_sources;  /* Number of sources */
static unsigned n_sources;  /* Number of sources */
static unsigned a_sources;  /* Allocated sources */
static unsigned a_sources;  /* Allocated sources */
 
 
static name_map_t *names;   /* Mapping of file names to sources */
static name_map_t *names;   /* Mapping of file names to sources */
static unsigned n_names;    /* Number of names */
static unsigned n_names;    /* Number of names */
static unsigned a_names;    /* Allocated names */
static unsigned a_names;    /* Allocated names */
 
 
/* This holds data summary information.  */
/* This holds data summary information.  */
 
 
static unsigned object_runs;
static unsigned object_runs;
static unsigned program_count;
static unsigned program_count;
 
 
static unsigned total_lines;
static unsigned total_lines;
static unsigned total_executed;
static unsigned total_executed;
 
 
/* Modification time of graph file.  */
/* Modification time of graph file.  */
 
 
static time_t bbg_file_time;
static time_t bbg_file_time;
 
 
/* Name and file pointer of the input file for the basic block graph.  */
/* Name and file pointer of the input file for the basic block graph.  */
 
 
static char *bbg_file_name;
static char *bbg_file_name;
 
 
/* Stamp of the bbg file */
/* Stamp of the bbg file */
static unsigned bbg_stamp;
static unsigned bbg_stamp;
 
 
/* Name and file pointer of the input file for the arc count data.  */
/* Name and file pointer of the input file for the arc count data.  */
 
 
static char *da_file_name;
static char *da_file_name;
 
 
/* Data file is missing.  */
/* Data file is missing.  */
 
 
static int no_data_file;
static int no_data_file;
 
 
/* If there is several input files, compute and display results after
/* 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
   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
   the same source file (eg inline subprograms in a .h file), the
   counts are added.  */
   counts are added.  */
 
 
static int multiple_files = 0;
static int multiple_files = 0;
 
 
/* Output branch probabilities.  */
/* Output branch probabilities.  */
 
 
static int flag_branches = 0;
static int flag_branches = 0;
 
 
/* Show unconditional branches too.  */
/* Show unconditional branches too.  */
static int flag_unconditional = 0;
static int flag_unconditional = 0;
 
 
/* Output a gcov file if this is true.  This is on by default, and can
/* Output a gcov file if this is true.  This is on by default, and can
   be turned off by the -n option.  */
   be turned off by the -n option.  */
 
 
static int flag_gcov_file = 1;
static int flag_gcov_file = 1;
 
 
/* Output progress indication if this is true.  This is off by default
/* Output progress indication if this is true.  This is off by default
   and can be turned on by the -d option.  */
   and can be turned on by the -d option.  */
 
 
static int flag_display_progress = 0;
static int flag_display_progress = 0;
 
 
/* For included files, make the gcov output file name include the name
/* 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,
   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.  */
   then the output file name is a.c##x.h.gcov instead of x.h.gcov.  */
 
 
static int flag_long_names = 0;
static int flag_long_names = 0;
 
 
/* Output count information for every basic block, not merely those
/* Output count information for every basic block, not merely those
   that contain line number information.  */
   that contain line number information.  */
 
 
static int flag_all_blocks = 0;
static int flag_all_blocks = 0;
 
 
/* Output summary info for each function.  */
/* Output summary info for each function.  */
 
 
static int flag_function_summary = 0;
static int flag_function_summary = 0;
 
 
/* Object directory file prefix.  This is the directory/file where the
/* Object directory file prefix.  This is the directory/file where the
   graph and data files are looked for, if nonzero.  */
   graph and data files are looked for, if nonzero.  */
 
 
static char *object_directory = 0;
static char *object_directory = 0;
 
 
/* Source directory prefix.  This is removed from source pathnames
/* Source directory prefix.  This is removed from source pathnames
   that match, when generating the output file name.  */
   that match, when generating the output file name.  */
 
 
static char *source_prefix = 0;
static char *source_prefix = 0;
static size_t source_length = 0;
static size_t source_length = 0;
 
 
/* Only show data for sources with relative pathnames.  Absolute ones
/* Only show data for sources with relative pathnames.  Absolute ones
   usually indicate a system header file, which although it may
   usually indicate a system header file, which although it may
   contain inline functions, is usually uninteresting.  */
   contain inline functions, is usually uninteresting.  */
static int flag_relative_only = 0;
static int flag_relative_only = 0;
 
 
/* Preserve all pathname components. Needed when object files and
/* Preserve all pathname components. Needed when object files and
   source files are in subdirectories. '/' is mangled as '#', '.' is
   source files are in subdirectories. '/' is mangled as '#', '.' is
   elided and '..' mangled to '^'.  */
   elided and '..' mangled to '^'.  */
 
 
static int flag_preserve_paths = 0;
static int flag_preserve_paths = 0;
 
 
/* Output the number of times a branch was taken as opposed to the percentage
/* Output the number of times a branch was taken as opposed to the percentage
   of times it was taken.  */
   of times it was taken.  */
 
 
static int flag_counts = 0;
static int flag_counts = 0;
 
 
/* Forward declarations.  */
/* Forward declarations.  */
static int process_args (int, char **);
static int process_args (int, char **);
static void print_usage (int) ATTRIBUTE_NORETURN;
static void print_usage (int) ATTRIBUTE_NORETURN;
static void print_version (void) ATTRIBUTE_NORETURN;
static void print_version (void) ATTRIBUTE_NORETURN;
static void process_file (const char *);
static void process_file (const char *);
static void generate_results (const char *);
static void generate_results (const char *);
static void create_file_names (const char *);
static void create_file_names (const char *);
static int name_search (const void *, const void *);
static int name_search (const void *, const void *);
static int name_sort (const void *, const void *);
static int name_sort (const void *, const void *);
static char *canonicalize_name (const char *);
static char *canonicalize_name (const char *);
static unsigned find_source (const char *);
static unsigned find_source (const char *);
static function_t *read_graph_file (void);
static function_t *read_graph_file (void);
static int read_count_file (function_t *);
static int read_count_file (function_t *);
static void solve_flow_graph (function_t *);
static void solve_flow_graph (function_t *);
static void find_exception_blocks (function_t *);
static void find_exception_blocks (function_t *);
static void add_branch_counts (coverage_t *, const arc_t *);
static void add_branch_counts (coverage_t *, const arc_t *);
static void add_line_counts (coverage_t *, function_t *);
static void add_line_counts (coverage_t *, function_t *);
static void executed_summary (unsigned, unsigned);
static void executed_summary (unsigned, unsigned);
static void function_summary (const coverage_t *, const char *);
static void function_summary (const coverage_t *, const char *);
static const char *format_gcov (gcov_type, gcov_type, int);
static const char *format_gcov (gcov_type, gcov_type, int);
static void accumulate_line_counts (source_t *);
static void accumulate_line_counts (source_t *);
static int output_branch_count (FILE *, int, const arc_t *);
static int output_branch_count (FILE *, int, const arc_t *);
static void output_lines (FILE *, const source_t *);
static void output_lines (FILE *, const source_t *);
static char *make_gcov_file_name (const char *, const char *);
static char *make_gcov_file_name (const char *, const char *);
static char *mangle_name (const char *, char *);
static char *mangle_name (const char *, char *);
static void release_structures (void);
static void release_structures (void);
static void release_function (function_t *);
static void release_function (function_t *);
extern int main (int, char **);
extern int main (int, char **);
 
 
int
int
main (int argc, char **argv)
main (int argc, char **argv)
{
{
  int argno;
  int argno;
  int first_arg;
  int first_arg;
  const char *p;
  const char *p;
 
 
  p = argv[0] + strlen (argv[0]);
  p = argv[0] + strlen (argv[0]);
  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
    --p;
    --p;
  progname = p;
  progname = p;
 
 
  xmalloc_set_program_name (progname);
  xmalloc_set_program_name (progname);
 
 
  /* Unlock the stdio streams.  */
  /* Unlock the stdio streams.  */
  unlock_std_streams ();
  unlock_std_streams ();
 
 
  gcc_init_libintl ();
  gcc_init_libintl ();
 
 
  diagnostic_initialize (global_dc, 0);
  diagnostic_initialize (global_dc, 0);
 
 
  /* Handle response files.  */
  /* Handle response files.  */
  expandargv (&argc, &argv);
  expandargv (&argc, &argv);
 
 
  a_names = 10;
  a_names = 10;
  names = XNEWVEC (name_map_t, a_names);
  names = XNEWVEC (name_map_t, a_names);
  a_sources = 10;
  a_sources = 10;
  sources = XNEWVEC (source_t, a_sources);
  sources = XNEWVEC (source_t, a_sources);
 
 
  argno = process_args (argc, argv);
  argno = process_args (argc, argv);
  if (optind == argc)
  if (optind == argc)
    print_usage (true);
    print_usage (true);
 
 
  if (argc - argno > 1)
  if (argc - argno > 1)
    multiple_files = 1;
    multiple_files = 1;
 
 
  first_arg = argno;
  first_arg = argno;
 
 
  for (; argno != argc; argno++)
  for (; argno != argc; argno++)
    {
    {
      if (flag_display_progress)
      if (flag_display_progress)
        printf("Processing file %d out of %d\n",
        printf("Processing file %d out of %d\n",
               argno - first_arg + 1, argc - first_arg);
               argno - first_arg + 1, argc - first_arg);
      process_file (argv[argno]);
      process_file (argv[argno]);
    }
    }
 
 
  generate_results (multiple_files ? NULL : argv[argc - 1]);
  generate_results (multiple_files ? NULL : argv[argc - 1]);
 
 
  release_structures ();
  release_structures ();
 
 
  return 0;
  return 0;
}
}


/* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
/* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
   otherwise the output of --help.  */
   otherwise the output of --help.  */
 
 
static void
static void
print_usage (int error_p)
print_usage (int error_p)
{
{
  FILE *file = error_p ? stderr : stdout;
  FILE *file = error_p ? stderr : stdout;
  int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
  int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
 
 
  fnotice (file, "Usage: gcov [OPTION]... SOURCE|OBJ...\n\n");
  fnotice (file, "Usage: gcov [OPTION]... SOURCE|OBJ...\n\n");
  fnotice (file, "Print code coverage information.\n\n");
  fnotice (file, "Print code coverage information.\n\n");
  fnotice (file, "  -h, --help                      Print this help, then exit\n");
  fnotice (file, "  -h, --help                      Print this help, then exit\n");
  fnotice (file, "  -v, --version                   Print version number, 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, "  -a, --all-blocks                Show information for every basic block\n");
  fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
  fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
  fnotice (file, "  -c, --branch-counts             Given counts of branches taken\n\
  fnotice (file, "  -c, --branch-counts             Given counts of branches taken\n\
                                    rather than percentages\n");
                                    rather than percentages\n");
  fnotice (file, "  -n, --no-output                 Do not create an output file\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\
  fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                    source files\n");
                                    source files\n");
  fnotice (file, "  -f, --function-summaries        Output summaries for each function\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, "  -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, "  -s, --source-prefix DIR         Source prefix to elide\n");
  fnotice (file, "  -r, --relative-only             Only show data for relative sources\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, "  -p, --preserve-paths            Preserve all pathname components\n");
  fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
  fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
  fnotice (file, "  -d, --display-progress          Display progress information\n");
  fnotice (file, "  -d, --display-progress          Display progress information\n");
  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
           bug_report_url);
           bug_report_url);
  exit (status);
  exit (status);
}
}
 
 
/* Print version information and exit.  */
/* Print version information and exit.  */
 
 
static void
static void
print_version (void)
print_version (void)
{
{
  fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
  fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
  fprintf (stdout, "Copyright %s 2012 Free Software Foundation, Inc.\n",
  fprintf (stdout, "Copyright %s 2012 Free Software Foundation, Inc.\n",
           _("(C)"));
           _("(C)"));
  fnotice (stdout,
  fnotice (stdout,
           _("This is free software; see the source for copying conditions.\n"
           _("This is free software; see the source for copying conditions.\n"
             "There is NO warranty; not even for MERCHANTABILITY or \n"
             "There is NO warranty; not even for MERCHANTABILITY or \n"
             "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
             "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
  exit (SUCCESS_EXIT_CODE);
  exit (SUCCESS_EXIT_CODE);
}
}
 
 
static const struct option options[] =
static const struct option options[] =
{
{
  { "help",                 no_argument,       NULL, 'h' },
  { "help",                 no_argument,       NULL, 'h' },
  { "version",              no_argument,       NULL, 'v' },
  { "version",              no_argument,       NULL, 'v' },
  { "all-blocks",           no_argument,       NULL, 'a' },
  { "all-blocks",           no_argument,       NULL, 'a' },
  { "branch-probabilities", no_argument,       NULL, 'b' },
  { "branch-probabilities", no_argument,       NULL, 'b' },
  { "branch-counts",        no_argument,       NULL, 'c' },
  { "branch-counts",        no_argument,       NULL, 'c' },
  { "no-output",            no_argument,       NULL, 'n' },
  { "no-output",            no_argument,       NULL, 'n' },
  { "long-file-names",      no_argument,       NULL, 'l' },
  { "long-file-names",      no_argument,       NULL, 'l' },
  { "function-summaries",   no_argument,       NULL, 'f' },
  { "function-summaries",   no_argument,       NULL, 'f' },
  { "preserve-paths",       no_argument,       NULL, 'p' },
  { "preserve-paths",       no_argument,       NULL, 'p' },
  { "relative-only",        no_argument,       NULL, 'r' },
  { "relative-only",        no_argument,       NULL, 'r' },
  { "object-directory",     required_argument, NULL, 'o' },
  { "object-directory",     required_argument, NULL, 'o' },
  { "object-file",          required_argument, NULL, 'o' },
  { "object-file",          required_argument, NULL, 'o' },
  { "source-prefix",        required_argument, NULL, 's' },
  { "source-prefix",        required_argument, NULL, 's' },
  { "unconditional-branches", no_argument,     NULL, 'u' },
  { "unconditional-branches", no_argument,     NULL, 'u' },
  { "display-progress",     no_argument,       NULL, 'd' },
  { "display-progress",     no_argument,       NULL, 'd' },
  { 0, 0, 0, 0 }
  { 0, 0, 0, 0 }
};
};
 
 
/* Process args, return index to first non-arg.  */
/* Process args, return index to first non-arg.  */
 
 
static int
static int
process_args (int argc, char **argv)
process_args (int argc, char **argv)
{
{
  int opt;
  int opt;
 
 
  while ((opt = getopt_long (argc, argv, "abcdfhlno:s:pruv", options, NULL)) != -1)
  while ((opt = getopt_long (argc, argv, "abcdfhlno:s:pruv", options, NULL)) != -1)
    {
    {
      switch (opt)
      switch (opt)
        {
        {
        case 'a':
        case 'a':
          flag_all_blocks = 1;
          flag_all_blocks = 1;
          break;
          break;
        case 'b':
        case 'b':
          flag_branches = 1;
          flag_branches = 1;
          break;
          break;
        case 'c':
        case 'c':
          flag_counts = 1;
          flag_counts = 1;
          break;
          break;
        case 'f':
        case 'f':
          flag_function_summary = 1;
          flag_function_summary = 1;
          break;
          break;
        case 'h':
        case 'h':
          print_usage (false);
          print_usage (false);
          /* print_usage will exit.  */
          /* print_usage will exit.  */
        case 'l':
        case 'l':
          flag_long_names = 1;
          flag_long_names = 1;
          break;
          break;
        case 'n':
        case 'n':
          flag_gcov_file = 0;
          flag_gcov_file = 0;
          break;
          break;
        case 'o':
        case 'o':
          object_directory = optarg;
          object_directory = optarg;
          break;
          break;
        case 's':
        case 's':
          source_prefix = optarg;
          source_prefix = optarg;
          source_length = strlen (source_prefix);
          source_length = strlen (source_prefix);
          break;
          break;
        case 'r':
        case 'r':
          flag_relative_only = 1;
          flag_relative_only = 1;
          break;
          break;
        case 'p':
        case 'p':
          flag_preserve_paths = 1;
          flag_preserve_paths = 1;
          break;
          break;
        case 'u':
        case 'u':
          flag_unconditional = 1;
          flag_unconditional = 1;
          break;
          break;
        case 'd':
        case 'd':
          flag_display_progress = 1;
          flag_display_progress = 1;
          break;
          break;
        case 'v':
        case 'v':
          print_version ();
          print_version ();
          /* print_version will exit.  */
          /* print_version will exit.  */
        default:
        default:
          print_usage (true);
          print_usage (true);
          /* print_usage will exit.  */
          /* print_usage will exit.  */
        }
        }
    }
    }
 
 
  return optind;
  return optind;
}
}
 
 
/* Process a single input file.  */
/* Process a single input file.  */
 
 
static void
static void
process_file (const char *file_name)
process_file (const char *file_name)
{
{
  function_t *fns;
  function_t *fns;
 
 
  create_file_names (file_name);
  create_file_names (file_name);
  fns = read_graph_file ();
  fns = read_graph_file ();
  if (!fns)
  if (!fns)
    return;
    return;
 
 
  read_count_file (fns);
  read_count_file (fns);
  while (fns)
  while (fns)
    {
    {
      function_t *fn = fns;
      function_t *fn = fns;
 
 
      fns = fn->next;
      fns = fn->next;
      fn->next = NULL;
      fn->next = NULL;
      if (fn->counts)
      if (fn->counts)
        {
        {
          unsigned src = fn->src;
          unsigned src = fn->src;
          unsigned line = fn->line;
          unsigned line = fn->line;
          unsigned block_no;
          unsigned block_no;
          function_t *probe, **prev;
          function_t *probe, **prev;
 
 
          /* Now insert it into the source file's list of
          /* Now insert it into the source file's list of
             functions. Normally functions will be encountered in
             functions. Normally functions will be encountered in
             ascending order, so a simple scan is quick.  Note we're
             ascending order, so a simple scan is quick.  Note we're
             building this list in reverse order.  */
             building this list in reverse order.  */
          for (prev = &sources[src].functions;
          for (prev = &sources[src].functions;
               (probe = *prev); prev = &probe->line_next)
               (probe = *prev); prev = &probe->line_next)
            if (probe->line <= line)
            if (probe->line <= line)
              break;
              break;
          fn->line_next = probe;
          fn->line_next = probe;
          *prev = fn;
          *prev = fn;
 
 
          /* Mark last line in files touched by function.  */
          /* Mark last line in files touched by function.  */
          for (block_no = 0; block_no != fn->num_blocks; block_no++)
          for (block_no = 0; block_no != fn->num_blocks; block_no++)
            {
            {
              unsigned *enc = fn->blocks[block_no].u.line.encoding;
              unsigned *enc = fn->blocks[block_no].u.line.encoding;
              unsigned num = fn->blocks[block_no].u.line.num;
              unsigned num = fn->blocks[block_no].u.line.num;
 
 
              for (; num--; enc++)
              for (; num--; enc++)
                if (!*enc)
                if (!*enc)
                  {
                  {
                    if (enc[1] != src)
                    if (enc[1] != src)
                      {
                      {
                        if (line >= sources[src].num_lines)
                        if (line >= sources[src].num_lines)
                          sources[src].num_lines = line + 1;
                          sources[src].num_lines = line + 1;
                        line = 0;
                        line = 0;
                        src = enc[1];
                        src = enc[1];
                      }
                      }
                    enc++;
                    enc++;
                    num--;
                    num--;
                  }
                  }
                else if (*enc > line)
                else if (*enc > line)
                  line = *enc;
                  line = *enc;
            }
            }
          if (line >= sources[src].num_lines)
          if (line >= sources[src].num_lines)
            sources[src].num_lines = line + 1;
            sources[src].num_lines = line + 1;
 
 
          solve_flow_graph (fn);
          solve_flow_graph (fn);
          if (fn->has_catch)
          if (fn->has_catch)
            find_exception_blocks (fn);
            find_exception_blocks (fn);
          *fn_end = fn;
          *fn_end = fn;
          fn_end = &fn->next;
          fn_end = &fn->next;
        }
        }
      else
      else
        /* The function was not in the executable -- some other
        /* The function was not in the executable -- some other
           instance must have been selected.  */
           instance must have been selected.  */
        release_function (fn);
        release_function (fn);
    }
    }
}
}
 
 
static void
static void
generate_results (const char *file_name)
generate_results (const char *file_name)
{
{
  unsigned ix;
  unsigned ix;
  source_t *src;
  source_t *src;
  function_t *fn;
  function_t *fn;
 
 
  for (ix = n_sources, src = sources; ix--; src++)
  for (ix = n_sources, src = sources; ix--; src++)
    if (src->num_lines)
    if (src->num_lines)
      src->lines = XCNEWVEC (line_t, src->num_lines);
      src->lines = XCNEWVEC (line_t, src->num_lines);
 
 
  for (fn = functions; fn; fn = fn->next)
  for (fn = functions; fn; fn = fn->next)
    {
    {
      coverage_t coverage;
      coverage_t coverage;
 
 
      memset (&coverage, 0, sizeof (coverage));
      memset (&coverage, 0, sizeof (coverage));
      coverage.name = fn->name;
      coverage.name = fn->name;
      add_line_counts (flag_function_summary ? &coverage : NULL, fn);
      add_line_counts (flag_function_summary ? &coverage : NULL, fn);
      if (flag_function_summary)
      if (flag_function_summary)
        {
        {
          function_summary (&coverage, "Function");
          function_summary (&coverage, "Function");
          fnotice (stdout, "\n");
          fnotice (stdout, "\n");
        }
        }
    }
    }
 
 
  if (file_name)
  if (file_name)
    {
    {
      name_map_t *name_map = (name_map_t *)bsearch
      name_map_t *name_map = (name_map_t *)bsearch
        (file_name, names, n_names, sizeof (*names), name_search);
        (file_name, names, n_names, sizeof (*names), name_search);
      if (name_map)
      if (name_map)
        file_name = sources[name_map->src].coverage.name;
        file_name = sources[name_map->src].coverage.name;
      else
      else
        file_name = canonicalize_name (file_name);
        file_name = canonicalize_name (file_name);
    }
    }
 
 
  for (ix = n_sources, src = sources; ix--; src++)
  for (ix = n_sources, src = sources; ix--; src++)
    {
    {
      if (flag_relative_only)
      if (flag_relative_only)
        {
        {
          /* Ignore this source, if it is an absolute path (after
          /* Ignore this source, if it is an absolute path (after
             source prefix removal).  */
             source prefix removal).  */
          char first = src->coverage.name[0];
          char first = src->coverage.name[0];
 
 
#if HAVE_DOS_BASED_FILE_SYSTEM
#if HAVE_DOS_BASED_FILE_SYSTEM
          if (first && src->coverage.name[1] == ':')
          if (first && src->coverage.name[1] == ':')
            first = src->coverage.name[2];
            first = src->coverage.name[2];
#endif
#endif
          if (IS_DIR_SEPARATOR (first))
          if (IS_DIR_SEPARATOR (first))
            continue;
            continue;
        }
        }
 
 
      accumulate_line_counts (src);
      accumulate_line_counts (src);
      function_summary (&src->coverage, "File");
      function_summary (&src->coverage, "File");
      total_lines += src->coverage.lines;
      total_lines += src->coverage.lines;
      total_executed += src->coverage.lines_executed;
      total_executed += src->coverage.lines_executed;
      if (flag_gcov_file)
      if (flag_gcov_file)
        {
        {
          char *gcov_file_name
          char *gcov_file_name
            = make_gcov_file_name (file_name, src->coverage.name);
            = make_gcov_file_name (file_name, src->coverage.name);
 
 
          if (src->coverage.lines)
          if (src->coverage.lines)
            {
            {
              FILE *gcov_file = fopen (gcov_file_name, "w");
              FILE *gcov_file = fopen (gcov_file_name, "w");
 
 
              if (gcov_file)
              if (gcov_file)
                {
                {
                  fnotice (stdout, "Creating '%s'\n", gcov_file_name);
                  fnotice (stdout, "Creating '%s'\n", gcov_file_name);
                  output_lines (gcov_file, src);
                  output_lines (gcov_file, src);
                  if (ferror (gcov_file))
                  if (ferror (gcov_file))
                    fnotice (stderr, "Error writing output file '%s'\n",
                    fnotice (stderr, "Error writing output file '%s'\n",
                             gcov_file_name);
                             gcov_file_name);
                  fclose (gcov_file);
                  fclose (gcov_file);
                }
                }
              else
              else
                fnotice (stderr, "Could not open output file '%s'\n",
                fnotice (stderr, "Could not open output file '%s'\n",
                         gcov_file_name);
                         gcov_file_name);
            }
            }
          else
          else
            {
            {
              unlink (gcov_file_name);
              unlink (gcov_file_name);
              fnotice (stdout, "Removing '%s'\n", gcov_file_name);
              fnotice (stdout, "Removing '%s'\n", gcov_file_name);
            }
            }
          free (gcov_file_name);
          free (gcov_file_name);
        }
        }
      fnotice (stdout, "\n");
      fnotice (stdout, "\n");
    }
    }
 
 
  if (!file_name)
  if (!file_name)
    executed_summary (total_lines, total_executed);
    executed_summary (total_lines, total_executed);
}
}
 
 
/* Release a function structure */
/* Release a function structure */
 
 
static void
static void
release_function (function_t *fn)
release_function (function_t *fn)
{
{
  unsigned ix;
  unsigned ix;
  block_t *block;
  block_t *block;
 
 
  for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
  for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
    {
    {
      arc_t *arc, *arc_n;
      arc_t *arc, *arc_n;
 
 
      for (arc = block->succ; arc; arc = arc_n)
      for (arc = block->succ; arc; arc = arc_n)
        {
        {
          arc_n = arc->succ_next;
          arc_n = arc->succ_next;
          free (arc);
          free (arc);
        }
        }
    }
    }
  free (fn->blocks);
  free (fn->blocks);
  free (fn->counts);
  free (fn->counts);
}
}
 
 
/* Release all memory used.  */
/* Release all memory used.  */
 
 
static void
static void
release_structures (void)
release_structures (void)
{
{
  unsigned ix;
  unsigned ix;
  function_t *fn;
  function_t *fn;
 
 
  for (ix = n_sources; ix--;)
  for (ix = n_sources; ix--;)
    free (sources[ix].lines);
    free (sources[ix].lines);
  free (sources);
  free (sources);
 
 
  for (ix = n_names; ix--;)
  for (ix = n_names; ix--;)
    free (names[ix].name);
    free (names[ix].name);
  free (names);
  free (names);
 
 
  while ((fn = functions))
  while ((fn = functions))
    {
    {
      functions = fn->next;
      functions = fn->next;
      release_function (fn);
      release_function (fn);
    }
    }
}
}
 
 
/* Generate the names of the graph and data files.  If OBJECT_DIRECTORY
/* Generate the names of the graph and data files.  If OBJECT_DIRECTORY
   is not specified, these are named from FILE_NAME sans extension.  If
   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
   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.
   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*
   Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file*
   and the data files are named from that.  */
   and the data files are named from that.  */
 
 
static void
static void
create_file_names (const char *file_name)
create_file_names (const char *file_name)
{
{
  char *cptr;
  char *cptr;
  char *name;
  char *name;
  int length = strlen (file_name);
  int length = strlen (file_name);
  int base;
  int base;
 
 
  /* Free previous file names.  */
  /* Free previous file names.  */
  free (bbg_file_name);
  free (bbg_file_name);
  free (da_file_name);
  free (da_file_name);
  da_file_name = bbg_file_name = NULL;
  da_file_name = bbg_file_name = NULL;
  bbg_file_time = 0;
  bbg_file_time = 0;
  bbg_stamp = 0;
  bbg_stamp = 0;
 
 
  if (object_directory && object_directory[0])
  if (object_directory && object_directory[0])
    {
    {
      struct stat status;
      struct stat status;
 
 
      length += strlen (object_directory) + 2;
      length += strlen (object_directory) + 2;
      name = XNEWVEC (char, length);
      name = XNEWVEC (char, length);
      name[0] = 0;
      name[0] = 0;
 
 
      base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
      base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
      strcat (name, object_directory);
      strcat (name, object_directory);
      if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
      if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
        strcat (name, "/");
        strcat (name, "/");
    }
    }
  else
  else
    {
    {
      name = XNEWVEC (char, length + 1);
      name = XNEWVEC (char, length + 1);
      strcpy (name, file_name);
      strcpy (name, file_name);
      base = 0;
      base = 0;
    }
    }
 
 
  if (base)
  if (base)
    {
    {
      /* Append source file name.  */
      /* Append source file name.  */
      const char *cptr = lbasename (file_name);
      const char *cptr = lbasename (file_name);
      strcat (name, cptr ? cptr : file_name);
      strcat (name, cptr ? cptr : file_name);
    }
    }
 
 
  /* Remove the extension.  */
  /* Remove the extension.  */
  cptr = strrchr (name, '.');
  cptr = strrchr (name, '.');
  if (cptr)
  if (cptr)
    *cptr = 0;
    *cptr = 0;
 
 
  length = strlen (name);
  length = strlen (name);
 
 
  bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
  bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
  strcpy (bbg_file_name, name);
  strcpy (bbg_file_name, name);
  strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
  strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
 
 
  da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
  da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
  strcpy (da_file_name, name);
  strcpy (da_file_name, name);
  strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
  strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
 
 
  free (name);
  free (name);
  return;
  return;
}
}
 
 
/* A is a string and B is a pointer to name_map_t.  Compare for file
/* A is a string and B is a pointer to name_map_t.  Compare for file
   name orderability.  */
   name orderability.  */
 
 
static int
static int
name_search (const void *a_, const void *b_)
name_search (const void *a_, const void *b_)
{
{
  const char *a = (const char *)a_;
  const char *a = (const char *)a_;
  const name_map_t *b = (const name_map_t *)b_;
  const name_map_t *b = (const name_map_t *)b_;
 
 
#if HAVE_DOS_BASED_FILE_SYSTEM
#if HAVE_DOS_BASED_FILE_SYSTEM
  return strcasecmp (a, b->name);
  return strcasecmp (a, b->name);
#else
#else
  return strcmp (a, b->name);
  return strcmp (a, b->name);
#endif
#endif
}
}
 
 
/* A and B are a pointer to name_map_t.  Compare for file name
/* A and B are a pointer to name_map_t.  Compare for file name
   orderability.  */
   orderability.  */
 
 
static int
static int
name_sort (const void *a_, const void *b_)
name_sort (const void *a_, const void *b_)
{
{
  const name_map_t *a = (const name_map_t *)a_;
  const name_map_t *a = (const name_map_t *)a_;
  return name_search (a->name, b_);
  return name_search (a->name, b_);
}
}
 
 
/* Find or create a source file structure for FILE_NAME. Copies
/* Find or create a source file structure for FILE_NAME. Copies
   FILE_NAME on creation */
   FILE_NAME on creation */
 
 
static unsigned
static unsigned
find_source (const char *file_name)
find_source (const char *file_name)
{
{
  name_map_t *name_map;
  name_map_t *name_map;
  char *canon;
  char *canon;
  unsigned idx;
  unsigned idx;
  struct stat status;
  struct stat status;
 
 
  if (!file_name)
  if (!file_name)
    file_name = "<unknown>";
    file_name = "<unknown>";
  name_map = (name_map_t *)bsearch
  name_map = (name_map_t *)bsearch
    (file_name, names, n_names, sizeof (*names), name_search);
    (file_name, names, n_names, sizeof (*names), name_search);
  if (name_map)
  if (name_map)
    {
    {
      idx = name_map->src;
      idx = name_map->src;
      goto check_date;
      goto check_date;
    }
    }
 
 
  if (n_names + 2 > a_names)
  if (n_names + 2 > a_names)
    {
    {
      /* Extend the name map array -- we'll be inserting one or two
      /* Extend the name map array -- we'll be inserting one or two
         entries.  */
         entries.  */
      a_names *= 2;
      a_names *= 2;
      name_map = XNEWVEC (name_map_t, a_names);
      name_map = XNEWVEC (name_map_t, a_names);
      memcpy (name_map, names, n_names * sizeof (*names));
      memcpy (name_map, names, n_names * sizeof (*names));
      free (names);
      free (names);
      names = name_map;
      names = name_map;
    }
    }
 
 
  /* Not found, try the canonical name. */
  /* Not found, try the canonical name. */
  canon = canonicalize_name (file_name);
  canon = canonicalize_name (file_name);
  name_map = (name_map_t *)bsearch
  name_map = (name_map_t *)bsearch
    (canon, names, n_names, sizeof (*names), name_search);
    (canon, names, n_names, sizeof (*names), name_search);
  if (!name_map)
  if (!name_map)
    {
    {
      /* Not found with canonical name, create a new source.  */
      /* Not found with canonical name, create a new source.  */
      source_t *src;
      source_t *src;
 
 
      if (n_sources == a_sources)
      if (n_sources == a_sources)
        {
        {
          a_sources *= 2;
          a_sources *= 2;
          src = XNEWVEC (source_t, a_sources);
          src = XNEWVEC (source_t, a_sources);
          memcpy (src, sources, n_sources * sizeof (*sources));
          memcpy (src, sources, n_sources * sizeof (*sources));
          free (sources);
          free (sources);
          sources = src;
          sources = src;
        }
        }
 
 
      idx = n_sources;
      idx = n_sources;
 
 
      name_map = &names[n_names++];
      name_map = &names[n_names++];
      name_map->name = canon;
      name_map->name = canon;
      name_map->src = idx;
      name_map->src = idx;
 
 
      src = &sources[n_sources++];
      src = &sources[n_sources++];
      memset (src, 0, sizeof (*src));
      memset (src, 0, sizeof (*src));
      src->name = canon;
      src->name = canon;
      src->coverage.name = src->name;
      src->coverage.name = src->name;
      if (source_length
      if (source_length
#if HAVE_DOS_BASED_FILE_SYSTEM
#if HAVE_DOS_BASED_FILE_SYSTEM
          /* You lose if separators don't match exactly in the
          /* You lose if separators don't match exactly in the
             prefix.  */
             prefix.  */
          && !strncasecmp (source_prefix, src->coverage.name, source_length)
          && !strncasecmp (source_prefix, src->coverage.name, source_length)
#else
#else
          && !strncmp (source_prefix, src->coverage.name, source_length)
          && !strncmp (source_prefix, src->coverage.name, source_length)
#endif
#endif
          && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
          && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
        src->coverage.name += source_length + 1;
        src->coverage.name += source_length + 1;
      if (!stat (src->name, &status))
      if (!stat (src->name, &status))
        src->file_time = status.st_mtime;
        src->file_time = status.st_mtime;
    }
    }
  else
  else
    idx = name_map->src;
    idx = name_map->src;
 
 
  if (name_search (file_name, name_map))
  if (name_search (file_name, name_map))
    {
    {
      /* Append the non-canonical name.  */
      /* Append the non-canonical name.  */
      name_map = &names[n_names++];
      name_map = &names[n_names++];
      name_map->name = xstrdup (file_name);
      name_map->name = xstrdup (file_name);
      name_map->src = idx;
      name_map->src = idx;
    }
    }
 
 
  /* Resort the name map.  */
  /* Resort the name map.  */
  qsort (names, n_names, sizeof (*names), name_sort);
  qsort (names, n_names, sizeof (*names), name_sort);
 
 
 check_date:
 check_date:
  if (sources[idx].file_time > bbg_file_time)
  if (sources[idx].file_time > bbg_file_time)
    {
    {
      static int info_emitted;
      static int info_emitted;
 
 
      fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
      fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
               file_name, bbg_file_name);
               file_name, bbg_file_name);
      if (!info_emitted)
      if (!info_emitted)
        {
        {
          fnotice (stderr,
          fnotice (stderr,
                   "(the message is only displayed one per source file)\n");
                   "(the message is only displayed one per source file)\n");
          info_emitted = 1;
          info_emitted = 1;
        }
        }
      sources[idx].file_time = 0;
      sources[idx].file_time = 0;
    }
    }
 
 
  return idx;
  return idx;
}
}
 
 
/* Read the graph file.  Return list of functions read -- in reverse order.  */
/* Read the graph file.  Return list of functions read -- in reverse order.  */
 
 
static function_t *
static function_t *
read_graph_file (void)
read_graph_file (void)
{
{
  unsigned version;
  unsigned version;
  unsigned current_tag = 0;
  unsigned current_tag = 0;
  function_t *fn = NULL;
  function_t *fn = NULL;
  function_t *fns = NULL;
  function_t *fns = NULL;
  function_t **fns_end = &fns;
  function_t **fns_end = &fns;
  unsigned src_idx = 0;
  unsigned src_idx = 0;
  unsigned ix;
  unsigned ix;
  unsigned tag;
  unsigned tag;
 
 
  if (!gcov_open (bbg_file_name, 1))
  if (!gcov_open (bbg_file_name, 1))
    {
    {
      fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
      fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
      return fns;
      return fns;
    }
    }
  bbg_file_time = gcov_time ();
  bbg_file_time = gcov_time ();
  if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
  if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
    {
    {
      fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
      fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
      gcov_close ();
      gcov_close ();
      return fns;
      return fns;
    }
    }
 
 
  version = gcov_read_unsigned ();
  version = gcov_read_unsigned ();
  if (version != GCOV_VERSION)
  if (version != GCOV_VERSION)
    {
    {
      char v[4], e[4];
      char v[4], e[4];
 
 
      GCOV_UNSIGNED2STRING (v, version);
      GCOV_UNSIGNED2STRING (v, version);
      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
 
      fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
      fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
               bbg_file_name, v, e);
               bbg_file_name, v, e);
    }
    }
  bbg_stamp = gcov_read_unsigned ();
  bbg_stamp = gcov_read_unsigned ();
 
 
  while ((tag = gcov_read_unsigned ()))
  while ((tag = gcov_read_unsigned ()))
    {
    {
      unsigned length = gcov_read_unsigned ();
      unsigned length = gcov_read_unsigned ();
      gcov_position_t base = gcov_position ();
      gcov_position_t base = gcov_position ();
 
 
      if (tag == GCOV_TAG_FUNCTION)
      if (tag == GCOV_TAG_FUNCTION)
        {
        {
          char *function_name;
          char *function_name;
          unsigned ident, lineno;
          unsigned ident, lineno;
          unsigned lineno_checksum, cfg_checksum;
          unsigned lineno_checksum, cfg_checksum;
 
 
          ident = gcov_read_unsigned ();
          ident = gcov_read_unsigned ();
          lineno_checksum = gcov_read_unsigned ();
          lineno_checksum = gcov_read_unsigned ();
          cfg_checksum = gcov_read_unsigned ();
          cfg_checksum = gcov_read_unsigned ();
          function_name = xstrdup (gcov_read_string ());
          function_name = xstrdup (gcov_read_string ());
          src_idx = find_source (gcov_read_string ());
          src_idx = find_source (gcov_read_string ());
          lineno = gcov_read_unsigned ();
          lineno = gcov_read_unsigned ();
 
 
          fn = XCNEW (function_t);
          fn = XCNEW (function_t);
          fn->name = function_name;
          fn->name = function_name;
          fn->ident = ident;
          fn->ident = ident;
          fn->lineno_checksum = lineno_checksum;
          fn->lineno_checksum = lineno_checksum;
          fn->cfg_checksum = cfg_checksum;
          fn->cfg_checksum = cfg_checksum;
          fn->src = src_idx;
          fn->src = src_idx;
          fn->line = lineno;
          fn->line = lineno;
 
 
          fn->line_next = NULL;
          fn->line_next = NULL;
          fn->next = NULL;
          fn->next = NULL;
          *fns_end = fn;
          *fns_end = fn;
          fns_end = &fn->next;
          fns_end = &fn->next;
          current_tag = tag;
          current_tag = tag;
        }
        }
      else if (fn && tag == GCOV_TAG_BLOCKS)
      else if (fn && tag == GCOV_TAG_BLOCKS)
        {
        {
          if (fn->blocks)
          if (fn->blocks)
            fnotice (stderr, "%s:already seen blocks for '%s'\n",
            fnotice (stderr, "%s:already seen blocks for '%s'\n",
                     bbg_file_name, fn->name);
                     bbg_file_name, fn->name);
          else
          else
            {
            {
              unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
              unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
              fn->num_blocks = num_blocks;
              fn->num_blocks = num_blocks;
 
 
              fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
              fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
              for (ix = 0; ix != num_blocks; ix++)
              for (ix = 0; ix != num_blocks; ix++)
                fn->blocks[ix].flags = gcov_read_unsigned ();
                fn->blocks[ix].flags = gcov_read_unsigned ();
            }
            }
        }
        }
      else if (fn && tag == GCOV_TAG_ARCS)
      else if (fn && tag == GCOV_TAG_ARCS)
        {
        {
          unsigned src = gcov_read_unsigned ();
          unsigned src = gcov_read_unsigned ();
          unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
          unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
          block_t *src_blk = &fn->blocks[src];
          block_t *src_blk = &fn->blocks[src];
          unsigned mark_catches = 0;
          unsigned mark_catches = 0;
          struct arc_info *arc;
          struct arc_info *arc;
 
 
          if (src >= fn->num_blocks || fn->blocks[src].succ)
          if (src >= fn->num_blocks || fn->blocks[src].succ)
            goto corrupt;
            goto corrupt;
 
 
          while (num_dests--)
          while (num_dests--)
            {
            {
              unsigned dest = gcov_read_unsigned ();
              unsigned dest = gcov_read_unsigned ();
              unsigned flags = gcov_read_unsigned ();
              unsigned flags = gcov_read_unsigned ();
 
 
              if (dest >= fn->num_blocks)
              if (dest >= fn->num_blocks)
                goto corrupt;
                goto corrupt;
              arc = XCNEW (arc_t);
              arc = XCNEW (arc_t);
 
 
              arc->dst = &fn->blocks[dest];
              arc->dst = &fn->blocks[dest];
              arc->src = src_blk;
              arc->src = src_blk;
 
 
              arc->count = 0;
              arc->count = 0;
              arc->count_valid = 0;
              arc->count_valid = 0;
              arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
              arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
              arc->fake = !!(flags & GCOV_ARC_FAKE);
              arc->fake = !!(flags & GCOV_ARC_FAKE);
              arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
              arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
 
 
              arc->succ_next = src_blk->succ;
              arc->succ_next = src_blk->succ;
              src_blk->succ = arc;
              src_blk->succ = arc;
              src_blk->num_succ++;
              src_blk->num_succ++;
 
 
              arc->pred_next = fn->blocks[dest].pred;
              arc->pred_next = fn->blocks[dest].pred;
              fn->blocks[dest].pred = arc;
              fn->blocks[dest].pred = arc;
              fn->blocks[dest].num_pred++;
              fn->blocks[dest].num_pred++;
 
 
              if (arc->fake)
              if (arc->fake)
                {
                {
                  if (src)
                  if (src)
                    {
                    {
                      /* Exceptional exit from this function, the
                      /* Exceptional exit from this function, the
                         source block must be a call.  */
                         source block must be a call.  */
                      fn->blocks[src].is_call_site = 1;
                      fn->blocks[src].is_call_site = 1;
                      arc->is_call_non_return = 1;
                      arc->is_call_non_return = 1;
                      mark_catches = 1;
                      mark_catches = 1;
                    }
                    }
                  else
                  else
                    {
                    {
                      /* Non-local return from a callee of this
                      /* Non-local return from a callee of this
                         function. The destination block is a setjmp.  */
                         function. The destination block is a setjmp.  */
                      arc->is_nonlocal_return = 1;
                      arc->is_nonlocal_return = 1;
                      fn->blocks[dest].is_nonlocal_return = 1;
                      fn->blocks[dest].is_nonlocal_return = 1;
                    }
                    }
                }
                }
 
 
              if (!arc->on_tree)
              if (!arc->on_tree)
                fn->num_counts++;
                fn->num_counts++;
            }
            }
 
 
          if (mark_catches)
          if (mark_catches)
            {
            {
              /* We have a fake exit from this block.  The other
              /* We have a fake exit from this block.  The other
                 non-fall through exits must be to catch handlers.
                 non-fall through exits must be to catch handlers.
                 Mark them as catch arcs.  */
                 Mark them as catch arcs.  */
 
 
              for (arc = src_blk->succ; arc; arc = arc->succ_next)
              for (arc = src_blk->succ; arc; arc = arc->succ_next)
                if (!arc->fake && !arc->fall_through)
                if (!arc->fake && !arc->fall_through)
                  {
                  {
                    arc->is_throw = 1;
                    arc->is_throw = 1;
                    fn->has_catch = 1;
                    fn->has_catch = 1;
                  }
                  }
            }
            }
        }
        }
      else if (fn && tag == GCOV_TAG_LINES)
      else if (fn && tag == GCOV_TAG_LINES)
        {
        {
          unsigned blockno = gcov_read_unsigned ();
          unsigned blockno = gcov_read_unsigned ();
          unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
          unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
 
 
          if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
          if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
            goto corrupt;
            goto corrupt;
 
 
          for (ix = 0; ;  )
          for (ix = 0; ;  )
            {
            {
              unsigned lineno = gcov_read_unsigned ();
              unsigned lineno = gcov_read_unsigned ();
 
 
              if (lineno)
              if (lineno)
                {
                {
                  if (!ix)
                  if (!ix)
                    {
                    {
                      line_nos[ix++] = 0;
                      line_nos[ix++] = 0;
                      line_nos[ix++] = src_idx;
                      line_nos[ix++] = src_idx;
                    }
                    }
                  line_nos[ix++] = lineno;
                  line_nos[ix++] = lineno;
                }
                }
              else
              else
                {
                {
                  const char *file_name = gcov_read_string ();
                  const char *file_name = gcov_read_string ();
 
 
                  if (!file_name)
                  if (!file_name)
                    break;
                    break;
                  src_idx = find_source (file_name);
                  src_idx = find_source (file_name);
                  line_nos[ix++] = 0;
                  line_nos[ix++] = 0;
                  line_nos[ix++] = src_idx;
                  line_nos[ix++] = src_idx;
                }
                }
            }
            }
 
 
          fn->blocks[blockno].u.line.encoding = line_nos;
          fn->blocks[blockno].u.line.encoding = line_nos;
          fn->blocks[blockno].u.line.num = ix;
          fn->blocks[blockno].u.line.num = ix;
        }
        }
      else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
      else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
        {
        {
          fn = NULL;
          fn = NULL;
          current_tag = 0;
          current_tag = 0;
        }
        }
      gcov_sync (base, length);
      gcov_sync (base, length);
      if (gcov_is_error ())
      if (gcov_is_error ())
        {
        {
        corrupt:;
        corrupt:;
          fnotice (stderr, "%s:corrupted\n", bbg_file_name);
          fnotice (stderr, "%s:corrupted\n", bbg_file_name);
          break;
          break;
        }
        }
    }
    }
  gcov_close ();
  gcov_close ();
 
 
  if (!fns)
  if (!fns)
    fnotice (stderr, "%s:no functions found\n", bbg_file_name);
    fnotice (stderr, "%s:no functions found\n", bbg_file_name);
 
 
  return fns;
  return fns;
}
}
 
 
/* Reads profiles from the count file and attach to each
/* Reads profiles from the count file and attach to each
   function. Return nonzero if fatal error.  */
   function. Return nonzero if fatal error.  */
 
 
static int
static int
read_count_file (function_t *fns)
read_count_file (function_t *fns)
{
{
  unsigned ix;
  unsigned ix;
  unsigned version;
  unsigned version;
  unsigned tag;
  unsigned tag;
  function_t *fn = NULL;
  function_t *fn = NULL;
  int error = 0;
  int error = 0;
 
 
  if (!gcov_open (da_file_name, 1))
  if (!gcov_open (da_file_name, 1))
    {
    {
      fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
      fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
               da_file_name);
               da_file_name);
      no_data_file = 1;
      no_data_file = 1;
      return 0;
      return 0;
    }
    }
  if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
  if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
    {
    {
      fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
      fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
    cleanup:;
    cleanup:;
      gcov_close ();
      gcov_close ();
      return 1;
      return 1;
    }
    }
  version = gcov_read_unsigned ();
  version = gcov_read_unsigned ();
  if (version != GCOV_VERSION)
  if (version != GCOV_VERSION)
    {
    {
      char v[4], e[4];
      char v[4], e[4];
 
 
      GCOV_UNSIGNED2STRING (v, version);
      GCOV_UNSIGNED2STRING (v, version);
      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
 
      fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
      fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
               da_file_name, v, e);
               da_file_name, v, e);
    }
    }
  tag = gcov_read_unsigned ();
  tag = gcov_read_unsigned ();
  if (tag != bbg_stamp)
  if (tag != bbg_stamp)
    {
    {
      fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
      fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
      goto cleanup;
      goto cleanup;
    }
    }
 
 
  while ((tag = gcov_read_unsigned ()))
  while ((tag = gcov_read_unsigned ()))
    {
    {
      unsigned length = gcov_read_unsigned ();
      unsigned length = gcov_read_unsigned ();
      unsigned long base = gcov_position ();
      unsigned long base = gcov_position ();
 
 
      if (tag == GCOV_TAG_PROGRAM_SUMMARY)
      if (tag == GCOV_TAG_PROGRAM_SUMMARY)
        {
        {
          struct gcov_summary summary;
          struct gcov_summary summary;
          gcov_read_summary (&summary);
          gcov_read_summary (&summary);
          object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
          object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
          program_count++;
          program_count++;
        }
        }
      else if (tag == GCOV_TAG_FUNCTION && !length)
      else if (tag == GCOV_TAG_FUNCTION && !length)
        ; /* placeholder  */
        ; /* placeholder  */
      else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
      else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
        {
        {
          unsigned ident;
          unsigned ident;
          struct function_info *fn_n;
          struct function_info *fn_n;
 
 
          /* Try to find the function in the list.  To speed up the
          /* Try to find the function in the list.  To speed up the
             search, first start from the last function found.  */
             search, first start from the last function found.  */
          ident = gcov_read_unsigned ();
          ident = gcov_read_unsigned ();
          fn_n = fns;
          fn_n = fns;
          for (fn = fn ? fn->next : NULL; ; fn = fn->next)
          for (fn = fn ? fn->next : NULL; ; fn = fn->next)
            {
            {
              if (fn)
              if (fn)
                ;
                ;
              else if ((fn = fn_n))
              else if ((fn = fn_n))
                fn_n = NULL;
                fn_n = NULL;
              else
              else
                {
                {
                  fnotice (stderr, "%s:unknown function '%u'\n",
                  fnotice (stderr, "%s:unknown function '%u'\n",
                           da_file_name, ident);
                           da_file_name, ident);
                  break;
                  break;
                }
                }
              if (fn->ident == ident)
              if (fn->ident == ident)
                break;
                break;
            }
            }
 
 
          if (!fn)
          if (!fn)
            ;
            ;
          else if (gcov_read_unsigned () != fn->lineno_checksum
          else if (gcov_read_unsigned () != fn->lineno_checksum
                   || gcov_read_unsigned () != fn->cfg_checksum)
                   || gcov_read_unsigned () != fn->cfg_checksum)
            {
            {
            mismatch:;
            mismatch:;
              fnotice (stderr, "%s:profile mismatch for '%s'\n",
              fnotice (stderr, "%s:profile mismatch for '%s'\n",
                       da_file_name, fn->name);
                       da_file_name, fn->name);
              goto cleanup;
              goto cleanup;
            }
            }
        }
        }
      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
        {
        {
          if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
          if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
            goto mismatch;
            goto mismatch;
 
 
          if (!fn->counts)
          if (!fn->counts)
            fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
            fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
 
 
          for (ix = 0; ix != fn->num_counts; ix++)
          for (ix = 0; ix != fn->num_counts; ix++)
            fn->counts[ix] += gcov_read_counter ();
            fn->counts[ix] += gcov_read_counter ();
        }
        }
      gcov_sync (base, length);
      gcov_sync (base, length);
      if ((error = gcov_is_error ()))
      if ((error = gcov_is_error ()))
        {
        {
          fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
          fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
                   da_file_name);
                   da_file_name);
          goto cleanup;
          goto cleanup;
        }
        }
    }
    }
 
 
  gcov_close ();
  gcov_close ();
  return 0;
  return 0;
}
}
 
 
/* Solve the flow graph. Propagate counts from the instrumented arcs
/* Solve the flow graph. Propagate counts from the instrumented arcs
   to the blocks and the uninstrumented arcs.  */
   to the blocks and the uninstrumented arcs.  */
 
 
static void
static void
solve_flow_graph (function_t *fn)
solve_flow_graph (function_t *fn)
{
{
  unsigned ix;
  unsigned ix;
  arc_t *arc;
  arc_t *arc;
  gcov_type *count_ptr = fn->counts;
  gcov_type *count_ptr = fn->counts;
  block_t *blk;
  block_t *blk;
  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
 
 
  /* The arcs were built in reverse order.  Fix that now.  */
  /* The arcs were built in reverse order.  Fix that now.  */
  for (ix = fn->num_blocks; ix--;)
  for (ix = fn->num_blocks; ix--;)
    {
    {
      arc_t *arc_p, *arc_n;
      arc_t *arc_p, *arc_n;
 
 
      for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
      for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
           arc_p = arc, arc = arc_n)
           arc_p = arc, arc = arc_n)
        {
        {
          arc_n = arc->succ_next;
          arc_n = arc->succ_next;
          arc->succ_next = arc_p;
          arc->succ_next = arc_p;
        }
        }
      fn->blocks[ix].succ = arc_p;
      fn->blocks[ix].succ = arc_p;
 
 
      for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
      for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
           arc_p = arc, arc = arc_n)
           arc_p = arc, arc = arc_n)
        {
        {
          arc_n = arc->pred_next;
          arc_n = arc->pred_next;
          arc->pred_next = arc_p;
          arc->pred_next = arc_p;
        }
        }
      fn->blocks[ix].pred = arc_p;
      fn->blocks[ix].pred = arc_p;
    }
    }
 
 
  if (fn->num_blocks < 2)
  if (fn->num_blocks < 2)
    fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
    fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
             bbg_file_name, fn->name);
             bbg_file_name, fn->name);
  else
  else
    {
    {
      if (fn->blocks[0].num_pred)
      if (fn->blocks[0].num_pred)
        fnotice (stderr, "%s:'%s' has arcs to entry block\n",
        fnotice (stderr, "%s:'%s' has arcs to entry block\n",
                 bbg_file_name, fn->name);
                 bbg_file_name, fn->name);
      else
      else
        /* We can't deduce the entry block counts from the lack of
        /* We can't deduce the entry block counts from the lack of
           predecessors.  */
           predecessors.  */
        fn->blocks[0].num_pred = ~(unsigned)0;
        fn->blocks[0].num_pred = ~(unsigned)0;
 
 
      if (fn->blocks[fn->num_blocks - 1].num_succ)
      if (fn->blocks[fn->num_blocks - 1].num_succ)
        fnotice (stderr, "%s:'%s' has arcs from exit block\n",
        fnotice (stderr, "%s:'%s' has arcs from exit block\n",
                 bbg_file_name, fn->name);
                 bbg_file_name, fn->name);
      else
      else
        /* Likewise, we can't deduce exit block counts from the lack
        /* Likewise, we can't deduce exit block counts from the lack
           of its successors.  */
           of its successors.  */
        fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0;
        fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0;
    }
    }
 
 
  /* Propagate the measured counts, this must be done in the same
  /* Propagate the measured counts, this must be done in the same
     order as the code in profile.c  */
     order as the code in profile.c  */
  for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
  for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
    {
    {
      block_t const *prev_dst = NULL;
      block_t const *prev_dst = NULL;
      int out_of_order = 0;
      int out_of_order = 0;
      int non_fake_succ = 0;
      int non_fake_succ = 0;
 
 
      for (arc = blk->succ; arc; arc = arc->succ_next)
      for (arc = blk->succ; arc; arc = arc->succ_next)
        {
        {
          if (!arc->fake)
          if (!arc->fake)
            non_fake_succ++;
            non_fake_succ++;
 
 
          if (!arc->on_tree)
          if (!arc->on_tree)
            {
            {
              if (count_ptr)
              if (count_ptr)
                arc->count = *count_ptr++;
                arc->count = *count_ptr++;
              arc->count_valid = 1;
              arc->count_valid = 1;
              blk->num_succ--;
              blk->num_succ--;
              arc->dst->num_pred--;
              arc->dst->num_pred--;
            }
            }
          if (prev_dst && prev_dst > arc->dst)
          if (prev_dst && prev_dst > arc->dst)
            out_of_order = 1;
            out_of_order = 1;
          prev_dst = arc->dst;
          prev_dst = arc->dst;
        }
        }
      if (non_fake_succ == 1)
      if (non_fake_succ == 1)
        {
        {
          /* If there is only one non-fake exit, it is an
          /* If there is only one non-fake exit, it is an
             unconditional branch.  */
             unconditional branch.  */
          for (arc = blk->succ; arc; arc = arc->succ_next)
          for (arc = blk->succ; arc; arc = arc->succ_next)
            if (!arc->fake)
            if (!arc->fake)
              {
              {
                arc->is_unconditional = 1;
                arc->is_unconditional = 1;
                /* If this block is instrumenting a call, it might be
                /* If this block is instrumenting a call, it might be
                   an artificial block. It is not artificial if it has
                   an artificial block. It is not artificial if it has
                   a non-fallthrough exit, or the destination of this
                   a non-fallthrough exit, or the destination of this
                   arc has more than one entry.  Mark the destination
                   arc has more than one entry.  Mark the destination
                   block as a return site, if none of those conditions
                   block as a return site, if none of those conditions
                   hold.  */
                   hold.  */
                if (blk->is_call_site && arc->fall_through
                if (blk->is_call_site && arc->fall_through
                    && arc->dst->pred == arc && !arc->pred_next)
                    && arc->dst->pred == arc && !arc->pred_next)
                  arc->dst->is_call_return = 1;
                  arc->dst->is_call_return = 1;
              }
              }
        }
        }
 
 
      /* Sort the successor arcs into ascending dst order. profile.c
      /* Sort the successor arcs into ascending dst order. profile.c
         normally produces arcs in the right order, but sometimes with
         normally produces arcs in the right order, but sometimes with
         one or two out of order.  We're not using a particularly
         one or two out of order.  We're not using a particularly
         smart sort.  */
         smart sort.  */
      if (out_of_order)
      if (out_of_order)
        {
        {
          arc_t *start = blk->succ;
          arc_t *start = blk->succ;
          unsigned changes = 1;
          unsigned changes = 1;
 
 
          while (changes)
          while (changes)
            {
            {
              arc_t *arc, *arc_p, *arc_n;
              arc_t *arc, *arc_p, *arc_n;
 
 
              changes = 0;
              changes = 0;
              for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
              for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
                {
                {
                  if (arc->dst > arc_n->dst)
                  if (arc->dst > arc_n->dst)
                    {
                    {
                      changes = 1;
                      changes = 1;
                      if (arc_p)
                      if (arc_p)
                        arc_p->succ_next = arc_n;
                        arc_p->succ_next = arc_n;
                      else
                      else
                        start = arc_n;
                        start = arc_n;
                      arc->succ_next = arc_n->succ_next;
                      arc->succ_next = arc_n->succ_next;
                      arc_n->succ_next = arc;
                      arc_n->succ_next = arc;
                      arc_p = arc_n;
                      arc_p = arc_n;
                    }
                    }
                  else
                  else
                    {
                    {
                      arc_p = arc;
                      arc_p = arc;
                      arc = arc_n;
                      arc = arc_n;
                    }
                    }
                }
                }
            }
            }
          blk->succ = start;
          blk->succ = start;
        }
        }
 
 
      /* Place it on the invalid chain, it will be ignored if that's
      /* Place it on the invalid chain, it will be ignored if that's
         wrong.  */
         wrong.  */
      blk->invalid_chain = 1;
      blk->invalid_chain = 1;
      blk->chain = invalid_blocks;
      blk->chain = invalid_blocks;
      invalid_blocks = blk;
      invalid_blocks = blk;
    }
    }
 
 
  while (invalid_blocks || valid_blocks)
  while (invalid_blocks || valid_blocks)
    {
    {
      while ((blk = invalid_blocks))
      while ((blk = invalid_blocks))
        {
        {
          gcov_type total = 0;
          gcov_type total = 0;
          const arc_t *arc;
          const arc_t *arc;
 
 
          invalid_blocks = blk->chain;
          invalid_blocks = blk->chain;
          blk->invalid_chain = 0;
          blk->invalid_chain = 0;
          if (!blk->num_succ)
          if (!blk->num_succ)
            for (arc = blk->succ; arc; arc = arc->succ_next)
            for (arc = blk->succ; arc; arc = arc->succ_next)
              total += arc->count;
              total += arc->count;
          else if (!blk->num_pred)
          else if (!blk->num_pred)
            for (arc = blk->pred; arc; arc = arc->pred_next)
            for (arc = blk->pred; arc; arc = arc->pred_next)
              total += arc->count;
              total += arc->count;
          else
          else
            continue;
            continue;
 
 
          blk->count = total;
          blk->count = total;
          blk->count_valid = 1;
          blk->count_valid = 1;
          blk->chain = valid_blocks;
          blk->chain = valid_blocks;
          blk->valid_chain = 1;
          blk->valid_chain = 1;
          valid_blocks = blk;
          valid_blocks = blk;
        }
        }
      while ((blk = valid_blocks))
      while ((blk = valid_blocks))
        {
        {
          gcov_type total;
          gcov_type total;
          arc_t *arc, *inv_arc;
          arc_t *arc, *inv_arc;
 
 
          valid_blocks = blk->chain;
          valid_blocks = blk->chain;
          blk->valid_chain = 0;
          blk->valid_chain = 0;
          if (blk->num_succ == 1)
          if (blk->num_succ == 1)
            {
            {
              block_t *dst;
              block_t *dst;
 
 
              total = blk->count;
              total = blk->count;
              inv_arc = NULL;
              inv_arc = NULL;
              for (arc = blk->succ; arc; arc = arc->succ_next)
              for (arc = blk->succ; arc; arc = arc->succ_next)
                {
                {
                  total -= arc->count;
                  total -= arc->count;
                  if (!arc->count_valid)
                  if (!arc->count_valid)
                    inv_arc = arc;
                    inv_arc = arc;
                }
                }
              dst = inv_arc->dst;
              dst = inv_arc->dst;
              inv_arc->count_valid = 1;
              inv_arc->count_valid = 1;
              inv_arc->count = total;
              inv_arc->count = total;
              blk->num_succ--;
              blk->num_succ--;
              dst->num_pred--;
              dst->num_pred--;
              if (dst->count_valid)
              if (dst->count_valid)
                {
                {
                  if (dst->num_pred == 1 && !dst->valid_chain)
                  if (dst->num_pred == 1 && !dst->valid_chain)
                    {
                    {
                      dst->chain = valid_blocks;
                      dst->chain = valid_blocks;
                      dst->valid_chain = 1;
                      dst->valid_chain = 1;
                      valid_blocks = dst;
                      valid_blocks = dst;
                    }
                    }
                }
                }
              else
              else
                {
                {
                  if (!dst->num_pred && !dst->invalid_chain)
                  if (!dst->num_pred && !dst->invalid_chain)
                    {
                    {
                      dst->chain = invalid_blocks;
                      dst->chain = invalid_blocks;
                      dst->invalid_chain = 1;
                      dst->invalid_chain = 1;
                      invalid_blocks = dst;
                      invalid_blocks = dst;
                    }
                    }
                }
                }
            }
            }
          if (blk->num_pred == 1)
          if (blk->num_pred == 1)
            {
            {
              block_t *src;
              block_t *src;
 
 
              total = blk->count;
              total = blk->count;
              inv_arc = NULL;
              inv_arc = NULL;
              for (arc = blk->pred; arc; arc = arc->pred_next)
              for (arc = blk->pred; arc; arc = arc->pred_next)
                {
                {
                  total -= arc->count;
                  total -= arc->count;
                  if (!arc->count_valid)
                  if (!arc->count_valid)
                    inv_arc = arc;
                    inv_arc = arc;
                }
                }
              src = inv_arc->src;
              src = inv_arc->src;
              inv_arc->count_valid = 1;
              inv_arc->count_valid = 1;
              inv_arc->count = total;
              inv_arc->count = total;
              blk->num_pred--;
              blk->num_pred--;
              src->num_succ--;
              src->num_succ--;
              if (src->count_valid)
              if (src->count_valid)
                {
                {
                  if (src->num_succ == 1 && !src->valid_chain)
                  if (src->num_succ == 1 && !src->valid_chain)
                    {
                    {
                      src->chain = valid_blocks;
                      src->chain = valid_blocks;
                      src->valid_chain = 1;
                      src->valid_chain = 1;
                      valid_blocks = src;
                      valid_blocks = src;
                    }
                    }
                }
                }
              else
              else
                {
                {
                  if (!src->num_succ && !src->invalid_chain)
                  if (!src->num_succ && !src->invalid_chain)
                    {
                    {
                      src->chain = invalid_blocks;
                      src->chain = invalid_blocks;
                      src->invalid_chain = 1;
                      src->invalid_chain = 1;
                      invalid_blocks = src;
                      invalid_blocks = src;
                    }
                    }
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  /* If the graph has been correctly solved, every block will have a
  /* If the graph has been correctly solved, every block will have a
     valid count.  */
     valid count.  */
  for (ix = 0; ix < fn->num_blocks; ix++)
  for (ix = 0; ix < fn->num_blocks; ix++)
    if (!fn->blocks[ix].count_valid)
    if (!fn->blocks[ix].count_valid)
      {
      {
        fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
        fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
                 bbg_file_name, fn->name);
                 bbg_file_name, fn->name);
        break;
        break;
      }
      }
}
}
 
 
/* Mark all the blocks only reachable via an incoming catch.  */
/* Mark all the blocks only reachable via an incoming catch.  */
 
 
static void
static void
find_exception_blocks (function_t *fn)
find_exception_blocks (function_t *fn)
{
{
  unsigned ix;
  unsigned ix;
  block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);
  block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);
 
 
  /* First mark all blocks as exceptional.  */
  /* First mark all blocks as exceptional.  */
  for (ix = fn->num_blocks; ix--;)
  for (ix = fn->num_blocks; ix--;)
    fn->blocks[ix].exceptional = 1;
    fn->blocks[ix].exceptional = 1;
 
 
  /* Now mark all the blocks reachable via non-fake edges */
  /* Now mark all the blocks reachable via non-fake edges */
  queue[0] = fn->blocks;
  queue[0] = fn->blocks;
  queue[0]->exceptional = 0;
  queue[0]->exceptional = 0;
  for (ix = 1; ix;)
  for (ix = 1; ix;)
    {
    {
      block_t *block = queue[--ix];
      block_t *block = queue[--ix];
      const arc_t *arc;
      const arc_t *arc;
 
 
      for (arc = block->succ; arc; arc = arc->succ_next)
      for (arc = block->succ; arc; arc = arc->succ_next)
        if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
        if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
          {
          {
            arc->dst->exceptional = 0;
            arc->dst->exceptional = 0;
            queue[ix++] = arc->dst;
            queue[ix++] = arc->dst;
          }
          }
    }
    }
}
}


 
 
/* Increment totals in COVERAGE according to arc ARC.  */
/* Increment totals in COVERAGE according to arc ARC.  */
 
 
static void
static void
add_branch_counts (coverage_t *coverage, const arc_t *arc)
add_branch_counts (coverage_t *coverage, const arc_t *arc)
{
{
  if (arc->is_call_non_return)
  if (arc->is_call_non_return)
    {
    {
      coverage->calls++;
      coverage->calls++;
      if (arc->src->count)
      if (arc->src->count)
        coverage->calls_executed++;
        coverage->calls_executed++;
    }
    }
  else if (!arc->is_unconditional)
  else if (!arc->is_unconditional)
    {
    {
      coverage->branches++;
      coverage->branches++;
      if (arc->src->count)
      if (arc->src->count)
        coverage->branches_executed++;
        coverage->branches_executed++;
      if (arc->count)
      if (arc->count)
        coverage->branches_taken++;
        coverage->branches_taken++;
    }
    }
}
}
 
 
/* Format a HOST_WIDE_INT as either a percent ratio, or absolute
/* Format a HOST_WIDE_INT as either a percent ratio, or absolute
   count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
   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
   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
   TOP==BOTTOM and only print 0% when TOP=0.  If dp < 0, then simply
   format TOP.  Return pointer to a static string.  */
   format TOP.  Return pointer to a static string.  */
 
 
static char const *
static char const *
format_gcov (gcov_type top, gcov_type bottom, int dp)
format_gcov (gcov_type top, gcov_type bottom, int dp)
{
{
  static char buffer[20];
  static char buffer[20];
 
 
  if (dp >= 0)
  if (dp >= 0)
    {
    {
      float ratio = bottom ? (float)top / bottom : 0;
      float ratio = bottom ? (float)top / bottom : 0;
      int ix;
      int ix;
      unsigned limit = 100;
      unsigned limit = 100;
      unsigned percent;
      unsigned percent;
 
 
      for (ix = dp; ix--; )
      for (ix = dp; ix--; )
        limit *= 10;
        limit *= 10;
 
 
      percent = (unsigned) (ratio * limit + (float)0.5);
      percent = (unsigned) (ratio * limit + (float)0.5);
      if (percent <= 0 && top)
      if (percent <= 0 && top)
        percent = 1;
        percent = 1;
      else if (percent >= limit && top != bottom)
      else if (percent >= limit && top != bottom)
        percent = limit - 1;
        percent = limit - 1;
      ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
      ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
      if (dp)
      if (dp)
        {
        {
          dp++;
          dp++;
          do
          do
            {
            {
              buffer[ix+1] = buffer[ix];
              buffer[ix+1] = buffer[ix];
              ix--;
              ix--;
            }
            }
          while (dp--);
          while (dp--);
          buffer[ix + 1] = '.';
          buffer[ix + 1] = '.';
        }
        }
    }
    }
  else
  else
    sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
    sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
 
 
  return buffer;
  return buffer;
}
}
 
 
/* Summary of execution */
/* Summary of execution */
 
 
static void
static void
executed_summary (unsigned lines, unsigned executed)
executed_summary (unsigned lines, unsigned executed)
{
{
  if (lines)
  if (lines)
    fnotice (stdout, "Lines executed:%s of %d\n",
    fnotice (stdout, "Lines executed:%s of %d\n",
             format_gcov (executed, lines, 2), lines);
             format_gcov (executed, lines, 2), lines);
  else
  else
    fnotice (stdout, "No executable lines\n");
    fnotice (stdout, "No executable lines\n");
}
}
 
 
/* Output summary info for a function or file.  */
/* Output summary info for a function or file.  */
 
 
static void
static void
function_summary (const coverage_t *coverage, const char *title)
function_summary (const coverage_t *coverage, const char *title)
{
{
  fnotice (stdout, "%s '%s'\n", title, coverage->name);
  fnotice (stdout, "%s '%s'\n", title, coverage->name);
  executed_summary (coverage->lines, coverage->lines_executed);
  executed_summary (coverage->lines, coverage->lines_executed);
 
 
  if (flag_branches)
  if (flag_branches)
    {
    {
      if (coverage->branches)
      if (coverage->branches)
        {
        {
          fnotice (stdout, "Branches executed:%s of %d\n",
          fnotice (stdout, "Branches executed:%s of %d\n",
                   format_gcov (coverage->branches_executed,
                   format_gcov (coverage->branches_executed,
                                coverage->branches, 2),
                                coverage->branches, 2),
                   coverage->branches);
                   coverage->branches);
          fnotice (stdout, "Taken at least once:%s of %d\n",
          fnotice (stdout, "Taken at least once:%s of %d\n",
                   format_gcov (coverage->branches_taken,
                   format_gcov (coverage->branches_taken,
                                coverage->branches, 2),
                                coverage->branches, 2),
                   coverage->branches);
                   coverage->branches);
        }
        }
      else
      else
        fnotice (stdout, "No branches\n");
        fnotice (stdout, "No branches\n");
      if (coverage->calls)
      if (coverage->calls)
        fnotice (stdout, "Calls executed:%s of %d\n",
        fnotice (stdout, "Calls executed:%s of %d\n",
                 format_gcov (coverage->calls_executed, coverage->calls, 2),
                 format_gcov (coverage->calls_executed, coverage->calls, 2),
                 coverage->calls);
                 coverage->calls);
      else
      else
        fnotice (stdout, "No calls\n");
        fnotice (stdout, "No calls\n");
    }
    }
}
}
 
 
/* Canonicalize the filename NAME by canonicalizing directory
/* Canonicalize the filename NAME by canonicalizing directory
   separators, eliding . components and resolving .. components
   separators, eliding . components and resolving .. components
   appropriately.  Always returns a unique string.  */
   appropriately.  Always returns a unique string.  */
 
 
static char *
static char *
canonicalize_name (const char *name)
canonicalize_name (const char *name)
{
{
  /* The canonical name cannot be longer than the incoming name.  */
  /* The canonical name cannot be longer than the incoming name.  */
  char *result = XNEWVEC (char, strlen (name) + 1);
  char *result = XNEWVEC (char, strlen (name) + 1);
  const char *base = name, *probe;
  const char *base = name, *probe;
  char *ptr = result;
  char *ptr = result;
  char *dd_base;
  char *dd_base;
  int slash = 0;
  int slash = 0;
 
 
#if HAVE_DOS_BASED_FILE_SYSTEM
#if HAVE_DOS_BASED_FILE_SYSTEM
  if (base[0] && base[1] == ':')
  if (base[0] && base[1] == ':')
    {
    {
      result[0] = base[0];
      result[0] = base[0];
      result[1] = ':';
      result[1] = ':';
      base += 2;
      base += 2;
      ptr += 2;
      ptr += 2;
    }
    }
#endif
#endif
  for (dd_base = ptr; *base; base = probe)
  for (dd_base = ptr; *base; base = probe)
    {
    {
      size_t len;
      size_t len;
 
 
      for (probe = base; *probe; probe++)
      for (probe = base; *probe; probe++)
        if (IS_DIR_SEPARATOR (*probe))
        if (IS_DIR_SEPARATOR (*probe))
          break;
          break;
 
 
      len = probe - base;
      len = probe - base;
      if (len == 1 && base[0] == '.')
      if (len == 1 && base[0] == '.')
        /* Elide a '.' directory */
        /* Elide a '.' directory */
        ;
        ;
      else if (len == 2 && base[0] == '.' && base[1] == '.')
      else if (len == 2 && base[0] == '.' && base[1] == '.')
        {
        {
          /* '..', we can only elide it and the previous directory, if
          /* '..', we can only elide it and the previous directory, if
             we're not a symlink.  */
             we're not a symlink.  */
          struct stat ATTRIBUTE_UNUSED buf;
          struct stat ATTRIBUTE_UNUSED buf;
 
 
          *ptr = 0;
          *ptr = 0;
          if (dd_base == ptr
          if (dd_base == ptr
#if defined (S_ISLNK)
#if defined (S_ISLNK)
              /* S_ISLNK is not POSIX.1-1996.  */
              /* S_ISLNK is not POSIX.1-1996.  */
              || stat (result, &buf) || S_ISLNK (buf.st_mode)
              || stat (result, &buf) || S_ISLNK (buf.st_mode)
#endif
#endif
              )
              )
            {
            {
              /* Cannot elide, or unreadable or a symlink.  */
              /* Cannot elide, or unreadable or a symlink.  */
              dd_base = ptr + 2 + slash;
              dd_base = ptr + 2 + slash;
              goto regular;
              goto regular;
            }
            }
          while (ptr != dd_base && *ptr != '/')
          while (ptr != dd_base && *ptr != '/')
            ptr--;
            ptr--;
          slash = ptr != result;
          slash = ptr != result;
        }
        }
      else
      else
        {
        {
        regular:
        regular:
          /* Regular pathname component.  */
          /* Regular pathname component.  */
          if (slash)
          if (slash)
            *ptr++ = '/';
            *ptr++ = '/';
          memcpy (ptr, base, len);
          memcpy (ptr, base, len);
          ptr += len;
          ptr += len;
          slash = 1;
          slash = 1;
        }
        }
 
 
      for (; IS_DIR_SEPARATOR (*probe); probe++)
      for (; IS_DIR_SEPARATOR (*probe); probe++)
        continue;
        continue;
    }
    }
  *ptr = 0;
  *ptr = 0;
 
 
  return result;
  return result;
}
}
 
 
/* Generate an output file name. INPUT_NAME is the canonicalized main
/* Generate an output file name. INPUT_NAME is the canonicalized main
   input file and SRC_NAME is the canonicalized file name.
   input file and SRC_NAME is the canonicalized file name.
   LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation.  With
   LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation.  With
   long_output_names we prepend the processed name of the input file
   long_output_names we prepend the processed name of the input file
   to each output name (except when the current source file is the
   to each output name (except when the current source file is the
   input file, so you don't get a double concatenation). The two
   input file, so you don't get a double concatenation). The two
   components are separated by '##'.  With preserve_paths we create a
   components are separated by '##'.  With preserve_paths we create a
   filename from all path components of the source file, replacing '/'
   filename from all path components of the source file, replacing '/'
   with '#', and .. with '^', without it we simply take the basename
   with '#', and .. with '^', without it we simply take the basename
   component.  (Remember, the canonicalized name will already have
   component.  (Remember, the canonicalized name will already have
   elided '.' components and converted \\ separators.)  */
   elided '.' components and converted \\ separators.)  */
 
 
static char *
static char *
make_gcov_file_name (const char *input_name, const char *src_name)
make_gcov_file_name (const char *input_name, const char *src_name)
{
{
  char *ptr;
  char *ptr;
  char *result;
  char *result;
 
 
  if (flag_long_names && input_name && strcmp (src_name, input_name))
  if (flag_long_names && input_name && strcmp (src_name, input_name))
    {
    {
      /* Generate the input filename part.  */
      /* Generate the input filename part.  */
      result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
      result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
 
 
      ptr = result;
      ptr = result;
      ptr = mangle_name (input_name, ptr);
      ptr = mangle_name (input_name, ptr);
      ptr[0] = ptr[1] = '#';
      ptr[0] = ptr[1] = '#';
      ptr += 2;
      ptr += 2;
    }
    }
  else
  else
    {
    {
      result = XNEWVEC (char, strlen (src_name) + 10);
      result = XNEWVEC (char, strlen (src_name) + 10);
      ptr = result;
      ptr = result;
    }
    }
 
 
  ptr = mangle_name (src_name, ptr);
  ptr = mangle_name (src_name, ptr);
  strcpy (ptr, ".gcov");
  strcpy (ptr, ".gcov");
 
 
  return result;
  return result;
}
}
 
 
static char *
static char *
mangle_name (char const *base, char *ptr)
mangle_name (char const *base, char *ptr)
{
{
  size_t len;
  size_t len;
 
 
  /* Generate the source filename part.  */
  /* Generate the source filename part.  */
  if (!flag_preserve_paths)
  if (!flag_preserve_paths)
    {
    {
      base = lbasename (base);
      base = lbasename (base);
      len = strlen (base);
      len = strlen (base);
      memcpy (ptr, base, len);
      memcpy (ptr, base, len);
      ptr += len;
      ptr += len;
    }
    }
  else
  else
    {
    {
      /* Convert '/' to '#', convert '..' to '^',
      /* Convert '/' to '#', convert '..' to '^',
         convert ':' to '~' on DOS based file system.  */
         convert ':' to '~' on DOS based file system.  */
      const char *probe;
      const char *probe;
 
 
#if HAVE_DOS_BASED_FILE_SYSTEM
#if HAVE_DOS_BASED_FILE_SYSTEM
      if (base[0] && base[1] == ':')
      if (base[0] && base[1] == ':')
        {
        {
          ptr[0] = base[0];
          ptr[0] = base[0];
          ptr[1] = '~';
          ptr[1] = '~';
          ptr += 2;
          ptr += 2;
          base += 2;
          base += 2;
        }
        }
#endif
#endif
      for (; *base; base = probe)
      for (; *base; base = probe)
        {
        {
          size_t len;
          size_t len;
 
 
          for (probe = base; *probe; probe++)
          for (probe = base; *probe; probe++)
            if (*probe == '/')
            if (*probe == '/')
              break;
              break;
          len = probe - base;
          len = probe - base;
          if (len == 2 && base[0] == '.' && base[1] == '.')
          if (len == 2 && base[0] == '.' && base[1] == '.')
            *ptr++ = '^';
            *ptr++ = '^';
          else
          else
            {
            {
              memcpy (ptr, base, len);
              memcpy (ptr, base, len);
              ptr += len;
              ptr += len;
            }
            }
          if (*probe)
          if (*probe)
            {
            {
              *ptr++ = '#';
              *ptr++ = '#';
              probe++;
              probe++;
            }
            }
        }
        }
    }
    }
 
 
  return ptr;
  return ptr;
}
}
 
 
/* Scan through the bb_data for each line in the block, increment
/* Scan through the bb_data for each line in the block, increment
   the line number execution count indicated by the execution count of
   the line number execution count indicated by the execution count of
   the appropriate basic block.  */
   the appropriate basic block.  */
 
 
static void
static void
add_line_counts (coverage_t *coverage, function_t *fn)
add_line_counts (coverage_t *coverage, function_t *fn)
{
{
  unsigned ix;
  unsigned ix;
  line_t *line = NULL; /* This is propagated from one iteration to the
  line_t *line = NULL; /* This is propagated from one iteration to the
                          next.  */
                          next.  */
 
 
  /* Scan each basic block.  */
  /* Scan each basic block.  */
  for (ix = 0; ix != fn->num_blocks; ix++)
  for (ix = 0; ix != fn->num_blocks; ix++)
    {
    {
      block_t *block = &fn->blocks[ix];
      block_t *block = &fn->blocks[ix];
      unsigned *encoding;
      unsigned *encoding;
      const source_t *src = NULL;
      const source_t *src = NULL;
      unsigned jx;
      unsigned jx;
 
 
      if (block->count && ix && ix + 1 != fn->num_blocks)
      if (block->count && ix && ix + 1 != fn->num_blocks)
        fn->blocks_executed++;
        fn->blocks_executed++;
      for (jx = 0, encoding = block->u.line.encoding;
      for (jx = 0, encoding = block->u.line.encoding;
           jx != block->u.line.num; jx++, encoding++)
           jx != block->u.line.num; jx++, encoding++)
        if (!*encoding)
        if (!*encoding)
          {
          {
            src = &sources[*++encoding];
            src = &sources[*++encoding];
            jx++;
            jx++;
          }
          }
        else
        else
          {
          {
            line = &src->lines[*encoding];
            line = &src->lines[*encoding];
 
 
            if (coverage)
            if (coverage)
              {
              {
                if (!line->exists)
                if (!line->exists)
                  coverage->lines++;
                  coverage->lines++;
                if (!line->count && block->count)
                if (!line->count && block->count)
                  coverage->lines_executed++;
                  coverage->lines_executed++;
              }
              }
            line->exists = 1;
            line->exists = 1;
            if (!block->exceptional)
            if (!block->exceptional)
              line->unexceptional = 1;
              line->unexceptional = 1;
            line->count += block->count;
            line->count += block->count;
          }
          }
      free (block->u.line.encoding);
      free (block->u.line.encoding);
      block->u.cycle.arc = NULL;
      block->u.cycle.arc = NULL;
      block->u.cycle.ident = ~0U;
      block->u.cycle.ident = ~0U;
 
 
      if (!ix || ix + 1 == fn->num_blocks)
      if (!ix || ix + 1 == fn->num_blocks)
        /* Entry or exit block */;
        /* Entry or exit block */;
      else if (flag_all_blocks)
      else if (flag_all_blocks)
        {
        {
          line_t *block_line = line;
          line_t *block_line = line;
 
 
          if (!block_line)
          if (!block_line)
            block_line = &sources[fn->src].lines[fn->line];
            block_line = &sources[fn->src].lines[fn->line];
 
 
          block->chain = block_line->u.blocks;
          block->chain = block_line->u.blocks;
          block_line->u.blocks = block;
          block_line->u.blocks = block;
        }
        }
      else if (flag_branches)
      else if (flag_branches)
        {
        {
          arc_t *arc;
          arc_t *arc;
 
 
          for (arc = block->succ; arc; arc = arc->succ_next)
          for (arc = block->succ; arc; arc = arc->succ_next)
            {
            {
              arc->line_next = line->u.branches;
              arc->line_next = line->u.branches;
              line->u.branches = arc;
              line->u.branches = arc;
              if (coverage && !arc->is_unconditional)
              if (coverage && !arc->is_unconditional)
                add_branch_counts (coverage, arc);
                add_branch_counts (coverage, arc);
            }
            }
        }
        }
    }
    }
  if (!line)
  if (!line)
    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
}
}
 
 
/* Accumulate the line counts of a file.  */
/* Accumulate the line counts of a file.  */
 
 
static void
static void
accumulate_line_counts (source_t *src)
accumulate_line_counts (source_t *src)
{
{
  line_t *line;
  line_t *line;
  function_t *fn, *fn_p, *fn_n;
  function_t *fn, *fn_p, *fn_n;
  unsigned ix;
  unsigned ix;
 
 
  /* Reverse the function order.  */
  /* Reverse the function order.  */
  for (fn = src->functions, fn_p = NULL; fn;
  for (fn = src->functions, fn_p = NULL; fn;
       fn_p = fn, fn = fn_n)
       fn_p = fn, fn = fn_n)
    {
    {
      fn_n = fn->line_next;
      fn_n = fn->line_next;
      fn->line_next = fn_p;
      fn->line_next = fn_p;
    }
    }
  src->functions = fn_p;
  src->functions = fn_p;
 
 
  for (ix = src->num_lines, line = src->lines; ix--; line++)
  for (ix = src->num_lines, line = src->lines; ix--; line++)
    {
    {
      if (!flag_all_blocks)
      if (!flag_all_blocks)
        {
        {
          arc_t *arc, *arc_p, *arc_n;
          arc_t *arc, *arc_p, *arc_n;
 
 
          /* Total and reverse the branch information.  */
          /* Total and reverse the branch information.  */
          for (arc = line->u.branches, arc_p = NULL; arc;
          for (arc = line->u.branches, arc_p = NULL; arc;
               arc_p = arc, arc = arc_n)
               arc_p = arc, arc = arc_n)
            {
            {
              arc_n = arc->line_next;
              arc_n = arc->line_next;
              arc->line_next = arc_p;
              arc->line_next = arc_p;
 
 
              add_branch_counts (&src->coverage, arc);
              add_branch_counts (&src->coverage, arc);
            }
            }
          line->u.branches = arc_p;
          line->u.branches = arc_p;
        }
        }
      else if (line->u.blocks)
      else if (line->u.blocks)
        {
        {
          /* The user expects the line count to be the number of times
          /* The user expects the line count to be the number of times
             a line has been executed. Simply summing the block count
             a line has been executed. Simply summing the block count
             will give an artificially high number.  The Right Thing
             will give an artificially high number.  The Right Thing
             is to sum the entry counts to the graph of blocks on this
             is to sum the entry counts to the graph of blocks on this
             line, then find the elementary cycles of the local graph
             line, then find the elementary cycles of the local graph
             and add the transition counts of those cycles.  */
             and add the transition counts of those cycles.  */
          block_t *block, *block_p, *block_n;
          block_t *block, *block_p, *block_n;
          gcov_type count = 0;
          gcov_type count = 0;
 
 
          /* Reverse the block information.  */
          /* Reverse the block information.  */
          for (block = line->u.blocks, block_p = NULL; block;
          for (block = line->u.blocks, block_p = NULL; block;
               block_p = block, block = block_n)
               block_p = block, block = block_n)
            {
            {
              block_n = block->chain;
              block_n = block->chain;
              block->chain = block_p;
              block->chain = block_p;
              block->u.cycle.ident = ix;
              block->u.cycle.ident = ix;
            }
            }
          line->u.blocks = block_p;
          line->u.blocks = block_p;
 
 
          /* Sum the entry arcs.  */
          /* Sum the entry arcs.  */
          for (block = line->u.blocks; block; block = block->chain)
          for (block = line->u.blocks; block; block = block->chain)
            {
            {
              arc_t *arc;
              arc_t *arc;
 
 
              for (arc = block->pred; arc; arc = arc->pred_next)
              for (arc = block->pred; arc; arc = arc->pred_next)
                {
                {
                  if (arc->src->u.cycle.ident != ix)
                  if (arc->src->u.cycle.ident != ix)
                    count += arc->count;
                    count += arc->count;
                  if (flag_branches)
                  if (flag_branches)
                    add_branch_counts (&src->coverage, arc);
                    add_branch_counts (&src->coverage, arc);
                }
                }
 
 
              /* Initialize the cs_count.  */
              /* Initialize the cs_count.  */
              for (arc = block->succ; arc; arc = arc->succ_next)
              for (arc = block->succ; arc; arc = arc->succ_next)
                arc->cs_count = arc->count;
                arc->cs_count = arc->count;
            }
            }
 
 
          /* Find the loops. This uses the algorithm described in
          /* Find the loops. This uses the algorithm described in
             Tiernan 'An Efficient Search Algorithm to Find the
             Tiernan 'An Efficient Search Algorithm to Find the
             Elementary Circuits of a Graph', CACM Dec 1970. We hold
             Elementary Circuits of a Graph', CACM Dec 1970. We hold
             the P array by having each block point to the arc that
             the P array by having each block point to the arc that
             connects to the previous block. The H array is implicitly
             connects to the previous block. The H array is implicitly
             held because of the arc ordering, and the block's
             held because of the arc ordering, and the block's
             previous arc pointer.
             previous arc pointer.
 
 
             Although the algorithm is O(N^3) for highly connected
             Although the algorithm is O(N^3) for highly connected
             graphs, at worst we'll have O(N^2), as most blocks have
             graphs, at worst we'll have O(N^2), as most blocks have
             only one or two exits. Most graphs will be small.
             only one or two exits. Most graphs will be small.
 
 
             For each loop we find, locate the arc with the smallest
             For each loop we find, locate the arc with the smallest
             transition count, and add that to the cumulative
             transition count, and add that to the cumulative
             count.  Decrease flow over the cycle and remove the arc
             count.  Decrease flow over the cycle and remove the arc
             from consideration.  */
             from consideration.  */
          for (block = line->u.blocks; block; block = block->chain)
          for (block = line->u.blocks; block; block = block->chain)
            {
            {
              block_t *head = block;
              block_t *head = block;
              arc_t *arc;
              arc_t *arc;
 
 
            next_vertex:;
            next_vertex:;
              arc = head->succ;
              arc = head->succ;
            current_vertex:;
            current_vertex:;
              while (arc)
              while (arc)
                {
                {
                  block_t *dst = arc->dst;
                  block_t *dst = arc->dst;
                  if (/* Already used that arc.  */
                  if (/* Already used that arc.  */
                      arc->cycle
                      arc->cycle
                      /* Not to same graph, or before first vertex.  */
                      /* Not to same graph, or before first vertex.  */
                      || dst->u.cycle.ident != ix
                      || dst->u.cycle.ident != ix
                      /* Already in path.  */
                      /* Already in path.  */
                      || dst->u.cycle.arc)
                      || dst->u.cycle.arc)
                    {
                    {
                      arc = arc->succ_next;
                      arc = arc->succ_next;
                      continue;
                      continue;
                    }
                    }
 
 
                  if (dst == block)
                  if (dst == block)
                    {
                    {
                      /* Found a closing arc.  */
                      /* Found a closing arc.  */
                      gcov_type cycle_count = arc->cs_count;
                      gcov_type cycle_count = arc->cs_count;
                      arc_t *cycle_arc = arc;
                      arc_t *cycle_arc = arc;
                      arc_t *probe_arc;
                      arc_t *probe_arc;
 
 
                      /* Locate the smallest arc count of the loop.  */
                      /* Locate the smallest arc count of the loop.  */
                      for (dst = head; (probe_arc = dst->u.cycle.arc);
                      for (dst = head; (probe_arc = dst->u.cycle.arc);
                           dst = probe_arc->src)
                           dst = probe_arc->src)
                        if (cycle_count > probe_arc->cs_count)
                        if (cycle_count > probe_arc->cs_count)
                          {
                          {
                            cycle_count = probe_arc->cs_count;
                            cycle_count = probe_arc->cs_count;
                            cycle_arc = probe_arc;
                            cycle_arc = probe_arc;
                          }
                          }
 
 
                      count += cycle_count;
                      count += cycle_count;
                      cycle_arc->cycle = 1;
                      cycle_arc->cycle = 1;
 
 
                      /* Remove the flow from the cycle.  */
                      /* Remove the flow from the cycle.  */
                      arc->cs_count -= cycle_count;
                      arc->cs_count -= cycle_count;
                      for (dst = head; (probe_arc = dst->u.cycle.arc);
                      for (dst = head; (probe_arc = dst->u.cycle.arc);
                           dst = probe_arc->src)
                           dst = probe_arc->src)
                        probe_arc->cs_count -= cycle_count;
                        probe_arc->cs_count -= cycle_count;
 
 
                      /* Unwind to the cyclic arc.  */
                      /* Unwind to the cyclic arc.  */
                      while (head != cycle_arc->src)
                      while (head != cycle_arc->src)
                        {
                        {
                          arc = head->u.cycle.arc;
                          arc = head->u.cycle.arc;
                          head->u.cycle.arc = NULL;
                          head->u.cycle.arc = NULL;
                          head = arc->src;
                          head = arc->src;
                        }
                        }
                      /* Move on.  */
                      /* Move on.  */
                      arc = arc->succ_next;
                      arc = arc->succ_next;
                      continue;
                      continue;
                    }
                    }
 
 
                  /* Add new block to chain.  */
                  /* Add new block to chain.  */
                  dst->u.cycle.arc = arc;
                  dst->u.cycle.arc = arc;
                  head = dst;
                  head = dst;
                  goto next_vertex;
                  goto next_vertex;
                }
                }
              /* We could not add another vertex to the path. Remove
              /* We could not add another vertex to the path. Remove
                 the last vertex from the list.  */
                 the last vertex from the list.  */
              arc = head->u.cycle.arc;
              arc = head->u.cycle.arc;
              if (arc)
              if (arc)
                {
                {
                  /* It was not the first vertex. Move onto next arc.  */
                  /* It was not the first vertex. Move onto next arc.  */
                  head->u.cycle.arc = NULL;
                  head->u.cycle.arc = NULL;
                  head = arc->src;
                  head = arc->src;
                  arc = arc->succ_next;
                  arc = arc->succ_next;
                  goto current_vertex;
                  goto current_vertex;
                }
                }
              /* Mark this block as unusable.  */
              /* Mark this block as unusable.  */
              block->u.cycle.ident = ~0U;
              block->u.cycle.ident = ~0U;
            }
            }
 
 
          line->count = count;
          line->count = count;
        }
        }
 
 
      if (line->exists)
      if (line->exists)
        {
        {
          src->coverage.lines++;
          src->coverage.lines++;
          if (line->count)
          if (line->count)
            src->coverage.lines_executed++;
            src->coverage.lines_executed++;
        }
        }
    }
    }
}
}
 
 
/* Output information about ARC number IX.  Returns nonzero if
/* Output information about ARC number IX.  Returns nonzero if
   anything is output.  */
   anything is output.  */
 
 
static int
static int
output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
{
{
  if (arc->is_call_non_return)
  if (arc->is_call_non_return)
    {
    {
      if (arc->src->count)
      if (arc->src->count)
        {
        {
          fnotice (gcov_file, "call   %2d returned %s\n", ix,
          fnotice (gcov_file, "call   %2d returned %s\n", ix,
                   format_gcov (arc->src->count - arc->count,
                   format_gcov (arc->src->count - arc->count,
                                arc->src->count, -flag_counts));
                                arc->src->count, -flag_counts));
        }
        }
      else
      else
        fnotice (gcov_file, "call   %2d never executed\n", ix);
        fnotice (gcov_file, "call   %2d never executed\n", ix);
    }
    }
  else if (!arc->is_unconditional)
  else if (!arc->is_unconditional)
    {
    {
      if (arc->src->count)
      if (arc->src->count)
        fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
        fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
                 format_gcov (arc->count, arc->src->count, -flag_counts),
                 format_gcov (arc->count, arc->src->count, -flag_counts),
                 arc->fall_through ? " (fallthrough)"
                 arc->fall_through ? " (fallthrough)"
                 : arc->is_throw ? " (throw)" : "");
                 : arc->is_throw ? " (throw)" : "");
      else
      else
        fnotice (gcov_file, "branch %2d never executed\n", ix);
        fnotice (gcov_file, "branch %2d never executed\n", ix);
    }
    }
  else if (flag_unconditional && !arc->dst->is_call_return)
  else if (flag_unconditional && !arc->dst->is_call_return)
    {
    {
      if (arc->src->count)
      if (arc->src->count)
        fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
        fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
                 format_gcov (arc->count, arc->src->count, -flag_counts));
                 format_gcov (arc->count, arc->src->count, -flag_counts));
      else
      else
        fnotice (gcov_file, "unconditional %2d never executed\n", ix);
        fnotice (gcov_file, "unconditional %2d never executed\n", ix);
    }
    }
  else
  else
    return 0;
    return 0;
  return 1;
  return 1;
 
 
}
}
 
 
static const char *
static const char *
read_line (FILE *file)
read_line (FILE *file)
{
{
  static char *string;
  static char *string;
  static size_t string_len;
  static size_t string_len;
  size_t pos = 0;
  size_t pos = 0;
  char *ptr;
  char *ptr;
 
 
  if (!string_len)
  if (!string_len)
    {
    {
      string_len = 200;
      string_len = 200;
      string = XNEWVEC (char, string_len);
      string = XNEWVEC (char, string_len);
    }
    }
 
 
  while ((ptr = fgets (string + pos, string_len - pos, file)))
  while ((ptr = fgets (string + pos, string_len - pos, file)))
    {
    {
      size_t len = strlen (string + pos);
      size_t len = strlen (string + pos);
 
 
      if (string[pos + len - 1] == '\n')
      if (string[pos + len - 1] == '\n')
        {
        {
          string[pos + len - 1] = 0;
          string[pos + len - 1] = 0;
          return string;
          return string;
        }
        }
      pos += len;
      pos += len;
      ptr = XNEWVEC (char, string_len * 2);
      ptr = XNEWVEC (char, string_len * 2);
      if (ptr)
      if (ptr)
        {
        {
          memcpy (ptr, string, pos);
          memcpy (ptr, string, pos);
          string = ptr;
          string = ptr;
          string_len += 2;
          string_len += 2;
        }
        }
      else
      else
        pos = 0;
        pos = 0;
    }
    }
 
 
  return pos ? string : NULL;
  return pos ? string : NULL;
}
}
 
 
/* Read in the source file one line at a time, and output that line to
/* 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
   the gcov file preceded by its execution count and other
   information.  */
   information.  */
 
 
static void
static void
output_lines (FILE *gcov_file, const source_t *src)
output_lines (FILE *gcov_file, const source_t *src)
{
{
  FILE *source_file;
  FILE *source_file;
  unsigned line_num;    /* current line number.  */
  unsigned line_num;    /* current line number.  */
  const line_t *line;           /* current line info ptr.  */
  const line_t *line;           /* current line info ptr.  */
  const char *retval = "";      /* status of source file reading.  */
  const char *retval = "";      /* status of source file reading.  */
  function_t *fn = NULL;
  function_t *fn = NULL;
 
 
  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
  if (!multiple_files)
  if (!multiple_files)
    {
    {
      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
               no_data_file ? "-" : da_file_name);
               no_data_file ? "-" : da_file_name);
      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
    }
    }
  fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
  fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
 
 
  source_file = fopen (src->name, "r");
  source_file = fopen (src->name, "r");
  if (!source_file)
  if (!source_file)
    {
    {
      fnotice (stderr, "Cannot open source file %s\n", src->name);
      fnotice (stderr, "Cannot open source file %s\n", src->name);
      retval = NULL;
      retval = NULL;
    }
    }
  else if (src->file_time == 0)
  else if (src->file_time == 0)
    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
 
 
  if (flag_branches)
  if (flag_branches)
    fn = src->functions;
    fn = src->functions;
 
 
  for (line_num = 1, line = &src->lines[line_num];
  for (line_num = 1, line = &src->lines[line_num];
       line_num < src->num_lines; line_num++, line++)
       line_num < src->num_lines; line_num++, line++)
    {
    {
      for (; fn && fn->line == line_num; fn = fn->line_next)
      for (; fn && fn->line == line_num; fn = fn->line_next)
        {
        {
          arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
          arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
          gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
          gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
 
 
          for (; arc; arc = arc->pred_next)
          for (; arc; arc = arc->pred_next)
            if (arc->fake)
            if (arc->fake)
              return_count -= arc->count;
              return_count -= arc->count;
 
 
          fprintf (gcov_file, "function %s", fn->name);
          fprintf (gcov_file, "function %s", fn->name);
          fprintf (gcov_file, " called %s",
          fprintf (gcov_file, " called %s",
                   format_gcov (fn->blocks[0].count, 0, -1));
                   format_gcov (fn->blocks[0].count, 0, -1));
          fprintf (gcov_file, " returned %s",
          fprintf (gcov_file, " returned %s",
                   format_gcov (return_count, fn->blocks[0].count, 0));
                   format_gcov (return_count, fn->blocks[0].count, 0));
          fprintf (gcov_file, " blocks executed %s",
          fprintf (gcov_file, " blocks executed %s",
                   format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
                   format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
          fprintf (gcov_file, "\n");
          fprintf (gcov_file, "\n");
        }
        }
 
 
      if (retval)
      if (retval)
        retval = read_line (source_file);
        retval = read_line (source_file);
 
 
      /* For lines which don't exist in the .bb file, print '-' before
      /* For lines which don't exist in the .bb file, print '-' before
         the source line.  For lines which exist but were never
         the source line.  For lines which exist but were never
         executed, print '#####' or '=====' before the source line.
         executed, print '#####' or '=====' before the source line.
         Otherwise, print the execution count before the source line.
         Otherwise, print the execution count before the source line.
         There are 16 spaces of indentation added before the source
         There are 16 spaces of indentation added before the source
         line so that tabs won't be messed up.  */
         line so that tabs won't be messed up.  */
      fprintf (gcov_file, "%9s:%5u:%s\n",
      fprintf (gcov_file, "%9s:%5u:%s\n",
               !line->exists ? "-" : line->count
               !line->exists ? "-" : line->count
               ? format_gcov (line->count, 0, -1)
               ? format_gcov (line->count, 0, -1)
               : line->unexceptional ? "#####" : "=====", line_num,
               : line->unexceptional ? "#####" : "=====", line_num,
               retval ? retval : "/*EOF*/");
               retval ? retval : "/*EOF*/");
 
 
      if (flag_all_blocks)
      if (flag_all_blocks)
        {
        {
          block_t *block;
          block_t *block;
          arc_t *arc;
          arc_t *arc;
          int ix, jx;
          int ix, jx;
 
 
          for (ix = jx = 0, block = line->u.blocks; block;
          for (ix = jx = 0, block = line->u.blocks; block;
               block = block->chain)
               block = block->chain)
            {
            {
              if (!block->is_call_return)
              if (!block->is_call_return)
                fprintf (gcov_file, "%9s:%5u-block %2d\n",
                fprintf (gcov_file, "%9s:%5u-block %2d\n",
                         !line->exists ? "-" : block->count
                         !line->exists ? "-" : block->count
                         ? format_gcov (block->count, 0, -1)
                         ? format_gcov (block->count, 0, -1)
                         : block->exceptional ? "%%%%%" : "$$$$$",
                         : block->exceptional ? "%%%%%" : "$$$$$",
                         line_num, ix++);
                         line_num, ix++);
              if (flag_branches)
              if (flag_branches)
                for (arc = block->succ; arc; arc = arc->succ_next)
                for (arc = block->succ; arc; arc = arc->succ_next)
                  jx += output_branch_count (gcov_file, jx, arc);
                  jx += output_branch_count (gcov_file, jx, arc);
            }
            }
        }
        }
      else if (flag_branches)
      else if (flag_branches)
        {
        {
          int ix;
          int ix;
          arc_t *arc;
          arc_t *arc;
 
 
          for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
          for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
            ix += output_branch_count (gcov_file, ix, arc);
            ix += output_branch_count (gcov_file, ix, arc);
        }
        }
    }
    }
 
 
  /* Handle all remaining source lines.  There may be lines after the
  /* Handle all remaining source lines.  There may be lines after the
     last line of code.  */
     last line of code.  */
  if (retval)
  if (retval)
    {
    {
      for (; (retval = read_line (source_file)); line_num++)
      for (; (retval = read_line (source_file)); line_num++)
        fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
        fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
    }
    }
 
 
  if (source_file)
  if (source_file)
    fclose (source_file);
    fclose (source_file);
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.