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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gcc-4.2.2/] [gcc/] [gcov.c] - Diff between revs 38 and 154

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

Rev 38 Rev 154
/* 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 Free Software Foundation, Inc.
   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 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.  */
 
 
/* ??? Does not correctly handle the case where two .bb files refer to
/* ??? Does not correctly handle the case where two .bb files refer to
   the same included source file.  For example, if one has a short
   the same included source file.  For example, if one has a short
   file containing only inline functions, which is then included in
   file containing only inline functions, which is then included in
   two other files, then there will be two .bb files which refer to
   two other files, then there will be two .bb files which refer to
   the include file, but there is no way to get the total execution
   the include file, but there is no way to get the total execution
   counts for the included file, can only get execution counts for one
   counts for the included file, can only get execution counts for one
   or the other of the including files. this can be fixed by --ratios
   or the other of the including files. this can be fixed by --ratios
   --long-file-names --preserve-paths and perl.  */
   --long-file-names --preserve-paths and perl.  */
 
 
/* 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 "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 bbg file is generated by -ftest-coverage option. The da file is
/* The bbg file is generated by -ftest-coverage option. The da 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.  */
 
 
/* 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.  */
 
 
#define STRING_SIZE 200
#define STRING_SIZE 200
 
 
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 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/setjump.  */
  /* Arc is for catch/setjump.  */
  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 : 13;
  unsigned flags : 13;
  unsigned count_valid : 1;
  unsigned count_valid : 1;
  unsigned valid_chain : 1;
  unsigned valid_chain : 1;
  unsigned invalid_chain : 1;
  unsigned invalid_chain : 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 checksum;
  unsigned checksum;
 
 
  /* 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.  */
  /* First line number.  */
  unsigned line;
  unsigned line;
  struct source_info *src;
  struct source_info *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;
} 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
{
{
  /* Name of source file.  */
  /* Name of source file.  */
  char *name;
  char *name;
  unsigned index;
  unsigned index;
 
 
  /* 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;
 
 
  /* Next source file.  */
  /* Next source file.  */
  struct source_info *next;
  struct source_info *next;
} source_t;
} source_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;
 
 
/* This points to the head of the sourcefile structure list.  */
/* This points to the head of the sourcefile structure list.  */
 
 
static source_t *sources;
static source_t *sources;
 
 
/* This holds data summary information.  */
/* This holds data summary information.  */
 
 
static struct gcov_summary object_summary;
static struct gcov_summary object_summary;
static unsigned program_count;
static unsigned program_count;
 
 
/* 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;
 
 
/* 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;
 
 
/* 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;
 
 
/* 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 void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
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 create_file_names (const char *);
static void create_file_names (const char *);
static source_t *find_source (const char *);
static source_t *find_source (const char *);
static int read_graph_file (void);
static int read_graph_file (void);
static int read_count_file (void);
static int read_count_file (void);
static void solve_flow_graph (function_t *);
static void solve_flow_graph (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 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 void release_structures (void);
static void release_structures (void);
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;
 
 
  /* Unlock the stdio streams.  */
  /* Unlock the stdio streams.  */
  unlock_std_streams ();
  unlock_std_streams ();
 
 
  gcc_init_libintl ();
  gcc_init_libintl ();
 
 
  argno = process_args (argc, argv);
  argno = process_args (argc, argv);
  if (optind == argc)
  if (optind == argc)
    print_usage (true);
    print_usage (true);
 
 
  for (; argno != argc; argno++)
  for (; argno != argc; argno++)
    {
    {
      release_structures ();
      release_structures ();
 
 
      process_file (argv[argno]);
      process_file (argv[argno]);
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
static void
static void
fnotice (FILE *file, const char *cmsgid, ...)
fnotice (FILE *file, const char *cmsgid, ...)
{
{
  va_list ap;
  va_list ap;
 
 
  va_start (ap, cmsgid);
  va_start (ap, cmsgid);
  vfprintf (file, _(cmsgid), ap);
  vfprintf (file, _(cmsgid), ap);
  va_end (ap);
  va_end (ap);
}
}


/* 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]... SOURCEFILE\n\n");
  fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\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, "  -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, "\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 (GCC) %s\n", version_string);
  fnotice (stdout, "gcov (GCC) %s\n", version_string);
  fprintf (stdout, "Copyright %s 2006 Free Software Foundation, Inc.\n",
  fprintf (stdout, "Copyright %s 2006 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' },
  { "object-directory",     required_argument, NULL, 'o' },
  { "object-directory",     required_argument, NULL, 'o' },
  { "object-file",          required_argument, NULL, 'o' },
  { "object-file",          required_argument, NULL, 'o' },
  { "unconditional-branches", no_argument,     NULL, 'u' },
  { "unconditional-branches", no_argument,     NULL, 'u' },
  { 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, "abcfhlno:puv", options, NULL)) != -1)
  while ((opt = getopt_long (argc, argv, "abcfhlno:puv", 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 '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 '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 source file.  */
/* Process a single source file.  */
 
 
static void
static void
process_file (const char *file_name)
process_file (const char *file_name)
{
{
  source_t *src;
  source_t *src;
  function_t *fn;
  function_t *fn;
 
 
  create_file_names (file_name);
  create_file_names (file_name);
  if (read_graph_file ())
  if (read_graph_file ())
    return;
    return;
 
 
  if (!functions)
  if (!functions)
    {
    {
      fnotice (stderr, "%s:no functions found\n", bbg_file_name);
      fnotice (stderr, "%s:no functions found\n", bbg_file_name);
      return;
      return;
    }
    }
 
 
  if (read_count_file ())
  if (read_count_file ())
    return;
    return;
 
 
  for (fn = functions; fn; fn = fn->next)
  for (fn = functions; fn; fn = fn->next)
    solve_flow_graph (fn);
    solve_flow_graph (fn);
  for (src = sources; src; src = src->next)
  for (src = sources; src; src = src->next)
    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");
        }
        }
    }
    }
 
 
  for (src = sources; src; src = src->next)
  for (src = sources; src; src = src->next)
    {
    {
      accumulate_line_counts (src);
      accumulate_line_counts (src);
      function_summary (&src->coverage, "File");
      function_summary (&src->coverage, "File");
      if (flag_gcov_file)
      if (flag_gcov_file)
        {
        {
          char *gcov_file_name = make_gcov_file_name (file_name, src->name);
          char *gcov_file_name = make_gcov_file_name (file_name, src->name);
          FILE *gcov_file = fopen (gcov_file_name, "w");
          FILE *gcov_file = fopen (gcov_file_name, "w");
 
 
          if (gcov_file)
          if (gcov_file)
            {
            {
              fnotice (stdout, "%s:creating '%s'\n",
              fnotice (stdout, "%s:creating '%s'\n",
                       src->name, gcov_file_name);
                       src->name, gcov_file_name);
              output_lines (gcov_file, src);
              output_lines (gcov_file, src);
              if (ferror (gcov_file))
              if (ferror (gcov_file))
                    fnotice (stderr, "%s:error writing output file '%s'\n",
                    fnotice (stderr, "%s:error writing output file '%s'\n",
                             src->name, gcov_file_name);
                             src->name, gcov_file_name);
              fclose (gcov_file);
              fclose (gcov_file);
            }
            }
          else
          else
            fnotice (stderr, "%s:could not open output file '%s'\n",
            fnotice (stderr, "%s:could not open output file '%s'\n",
                     src->name, gcov_file_name);
                     src->name, gcov_file_name);
          free (gcov_file_name);
          free (gcov_file_name);
        }
        }
      fnotice (stdout, "\n");
      fnotice (stdout, "\n");
    }
    }
}
}
 
 
/* Release all memory used.  */
/* Release all memory used.  */
 
 
static void
static void
release_structures (void)
release_structures (void)
{
{
  function_t *fn;
  function_t *fn;
  source_t *src;
  source_t *src;
 
 
  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;
 
 
  while ((src = sources))
  while ((src = sources))
    {
    {
      sources = src->next;
      sources = src->next;
 
 
      free (src->name);
      free (src->name);
      free (src->lines);
      free (src->lines);
    }
    }
 
 
  while ((fn = functions))
  while ((fn = functions))
    {
    {
      unsigned ix;
      unsigned ix;
      block_t *block;
      block_t *block;
 
 
      functions = fn->next;
      functions = fn->next;
      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);
    }
    }
}
}
 
 
/* 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 looked for in the current directory,
   is not specified, these are looked for in the current directory,
   and named from the basename of the FILE_NAME sans extension. If
   and named from the basename of the FILE_NAME sans extension. If
   OBJECT_DIRECTORY is specified and is a directory, the files are in
   OBJECT_DIRECTORY is specified and is a directory, the files are in
   that directory, but named from the basename of the FILE_NAME, sans
   that directory, but named from the basename of the FILE_NAME, sans
   extension. Otherwise OBJECT_DIRECTORY is taken to be the name of
   extension. Otherwise OBJECT_DIRECTORY is taken to be the name of
   the object *file*, and the data files are named from that.  */
   the object *file*, 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;
 
 
  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 && name[strlen (name) - 1] != '/')
      if (base && name[strlen (name) - 1] != '/')
        strcat (name, "/");
        strcat (name, "/");
    }
    }
  else
  else
    {
    {
      name = XNEWVEC (char, length + 1);
      name = XNEWVEC (char, length + 1);
      name[0] = 0;
      name[0] = 0;
      base = 1;
      base = 1;
    }
    }
 
 
  if (base)
  if (base)
    {
    {
      /* Append source file name.  */
      /* Append source file name.  */
      cptr = strrchr (file_name, '/');
      cptr = strrchr (file_name, '/');
      strcat (name, cptr ? cptr + 1 : file_name);
      strcat (name, cptr ? cptr + 1 : 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;
}
}
 
 
/* 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 source_t *
static source_t *
find_source (const char *file_name)
find_source (const char *file_name)
{
{
  source_t *src;
  source_t *src;
 
 
  if (!file_name)
  if (!file_name)
    file_name = "<unknown>";
    file_name = "<unknown>";
 
 
  for (src = sources; src; src = src->next)
  for (src = sources; src; src = src->next)
    if (!strcmp (file_name, src->name))
    if (!strcmp (file_name, src->name))
      return src;
      return src;
 
 
  src = XCNEW (source_t);
  src = XCNEW (source_t);
  src->name = xstrdup (file_name);
  src->name = xstrdup (file_name);
  src->coverage.name = src->name;
  src->coverage.name = src->name;
  src->index = sources ? sources->index + 1 : 1;
  src->index = sources ? sources->index + 1 : 1;
  src->next = sources;
  src->next = sources;
  sources = src;
  sources = src;
 
 
  return src;
  return src;
}
}
 
 
/* Read the graph file. Return nonzero on fatal error.  */
/* Read the graph file. Return nonzero on fatal error.  */
 
 
static int
static int
read_graph_file (void)
read_graph_file (void)
{
{
  unsigned version;
  unsigned version;
  unsigned current_tag = 0;
  unsigned current_tag = 0;
  struct function_info *fn = NULL;
  struct function_info *fn = NULL;
  source_t *src = NULL;
  source_t *src = NULL;
  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 1;
      return 1;
    }
    }
  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 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 '%.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, checksum, lineno;
          unsigned ident, checksum, lineno;
          source_t *src;
          source_t *src;
          function_t *probe, *prev;
          function_t *probe, *prev;
 
 
          ident = gcov_read_unsigned ();
          ident = gcov_read_unsigned ();
          checksum = gcov_read_unsigned ();
          checksum = gcov_read_unsigned ();
          function_name = xstrdup (gcov_read_string ());
          function_name = xstrdup (gcov_read_string ());
          src = find_source (gcov_read_string ());
          src = 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->checksum = checksum;
          fn->checksum = checksum;
          fn->src = src;
          fn->src = src;
          fn->line = lineno;
          fn->line = lineno;
 
 
          fn->next = functions;
          fn->next = functions;
          functions = fn;
          functions = fn;
          current_tag = tag;
          current_tag = tag;
 
 
          if (lineno >= src->num_lines)
          if (lineno >= src->num_lines)
            src->num_lines = lineno + 1;
            src->num_lines = lineno + 1;
          /* 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.  */
             ascending order, so a simple scan is quick.  */
          for (probe = src->functions, prev = NULL;
          for (probe = src->functions, prev = NULL;
               probe && probe->line > lineno;
               probe && probe->line > lineno;
               prev = probe, probe = probe->line_next)
               prev = probe, probe = probe->line_next)
            continue;
            continue;
          fn->line_next = probe;
          fn->line_next = probe;
          if (prev)
          if (prev)
            prev->line_next = fn;
            prev->line_next = fn;
          else
          else
            src->functions = fn;
            src->functions = fn;
        }
        }
      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);
 
 
          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--)
            {
            {
              struct arc_info *arc;
              struct arc_info *arc;
              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 = &fn->blocks[src];
              arc->src = &fn->blocks[src];
 
 
              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 = fn->blocks[src].succ;
              arc->succ_next = fn->blocks[src].succ;
              fn->blocks[src].succ = arc;
              fn->blocks[src].succ = arc;
              fn->blocks[src].num_succ++;
              fn->blocks[src].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;
                    }
                    }
                  else
                  else
                    {
                    {
                      /* Non-local return from a callee of this
                      /* Non-local return from a callee of this
                         function. The destination block is a catch or
                         function. The destination block is a catch or
                         setjmp.  */
                         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++;
            }
            }
        }
        }
      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->index;
                      line_nos[ix++] = src->index;
                    }
                    }
                  line_nos[ix++] = lineno;
                  line_nos[ix++] = lineno;
                  if (lineno >= src->num_lines)
                  if (lineno >= src->num_lines)
                    src->num_lines = lineno + 1;
                    src->num_lines = lineno + 1;
                }
                }
              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 = find_source (file_name);
                  src = find_source (file_name);
 
 
                  line_nos[ix++] = 0;
                  line_nos[ix++] = 0;
                  line_nos[ix++] = src->index;
                  line_nos[ix++] = src->index;
                }
                }
            }
            }
 
 
          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);
          gcov_close ();
          gcov_close ();
          return 1;
          return 1;
        }
        }
    }
    }
  gcov_close ();
  gcov_close ();
 
 
  /* We built everything backwards, so nreverse them all.  */
  /* We built everything backwards, so nreverse them all.  */
 
 
  /* Reverse sources. Not strictly necessary, but we'll then process
  /* Reverse sources. Not strictly necessary, but we'll then process
     them in the 'expected' order.  */
     them in the 'expected' order.  */
  {
  {
    source_t *src, *src_p, *src_n;
    source_t *src, *src_p, *src_n;
 
 
    for (src_p = NULL, src = sources; src; src_p = src, src = src_n)
    for (src_p = NULL, src = sources; src; src_p = src, src = src_n)
      {
      {
        src_n = src->next;
        src_n = src->next;
        src->next = src_p;
        src->next = src_p;
      }
      }
    sources =  src_p;
    sources =  src_p;
  }
  }
 
 
  /* Reverse functions.  */
  /* Reverse functions.  */
  {
  {
    function_t *fn, *fn_p, *fn_n;
    function_t *fn, *fn_p, *fn_n;
 
 
    for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
    for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
      {
      {
        unsigned ix;
        unsigned ix;
 
 
        fn_n = fn->next;
        fn_n = fn->next;
        fn->next = fn_p;
        fn->next = fn_p;
 
 
        /* Reverse the arcs.  */
        /* Reverse the arcs.  */
        for (ix = fn->num_blocks; ix--;)
        for (ix = fn->num_blocks; ix--;)
          {
          {
            arc_t *arc, *arc_p, *arc_n;
            arc_t *arc, *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;
          }
          }
      }
      }
    functions = fn_p;
    functions = fn_p;
  }
  }
  return 0;
  return 0;
}
}
 
 
/* 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 (void)
read_count_file (void)
{
{
  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_OBJECT_SUMMARY)
      if (tag == GCOV_TAG_OBJECT_SUMMARY)
        gcov_read_summary (&object_summary);
        gcov_read_summary (&object_summary);
      else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
      else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
        program_count++;
        program_count++;
      else if (tag == GCOV_TAG_FUNCTION)
      else if (tag == GCOV_TAG_FUNCTION)
        {
        {
          unsigned ident = gcov_read_unsigned ();
          unsigned ident = gcov_read_unsigned ();
          struct function_info *fn_n = functions;
          struct function_info *fn_n = functions;
 
 
          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->checksum)
          else if (gcov_read_unsigned () != fn->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.  */
 
 
  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;
      }
      }
}
}
 
 


 
 
/* 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;
}
}
 
 
 
 
/* Output summary info for a function.  */
/* Output summary info for a function.  */
 
 
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);
 
 
  if (coverage->lines)
  if (coverage->lines)
    fnotice (stdout, "Lines executed:%s of %d\n",
    fnotice (stdout, "Lines executed:%s of %d\n",
             format_gcov (coverage->lines_executed, coverage->lines, 2),
             format_gcov (coverage->lines_executed, coverage->lines, 2),
             coverage->lines);
             coverage->lines);
  else
  else
    fnotice (stdout, "No executable lines\n");
    fnotice (stdout, "No executable lines\n");
 
 
  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");
    }
    }
}
}
 
 
/* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS
/* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS
   affect name generation. With preserve_paths we create a filename
   affect name generation. With preserve_paths we create a filename
   from all path components of the source file, replacing '/' with
   from all path components of the source file, replacing '/' with
   '#', without it we simply take the basename component. With
   '#', without it we simply take the basename component. 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 '##'. Also '.' filename components are
   components are separated by '##'. Also '.' filename components are
   removed and '..'  components are renamed to '^'.  */
   removed and '..'  components are renamed to '^'.  */
 
 
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 *cptr;
  char *cptr;
  char *name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10);
  char *name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10);
 
 
  name[0] = 0;
  name[0] = 0;
  if (flag_long_names && strcmp (src_name, input_name))
  if (flag_long_names && strcmp (src_name, input_name))
    {
    {
      /* Generate the input filename part.  */
      /* Generate the input filename part.  */
      cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/');
      cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/');
      strcat (name, cptr ? cptr + 1 : input_name);
      strcat (name, cptr ? cptr + 1 : input_name);
      strcat (name, "##");
      strcat (name, "##");
    }
    }
 
 
  /* Generate the source filename part.  */
  /* Generate the source filename part.  */
  cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
  cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
  strcat (name, cptr ? cptr + 1 : src_name);
  strcat (name, cptr ? cptr + 1 : src_name);
 
 
  if (flag_preserve_paths)
  if (flag_preserve_paths)
    {
    {
      /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
      /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
      char *prev;
      char *prev;
 
 
      for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
      for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
        {
        {
          unsigned shift = 0;
          unsigned shift = 0;
 
 
          if (prev + 1 == cptr && prev[0] == '.')
          if (prev + 1 == cptr && prev[0] == '.')
            {
            {
              /* Remove '.' */
              /* Remove '.' */
              shift = 2;
              shift = 2;
            }
            }
          else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
          else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
            {
            {
              /* Convert '..' */
              /* Convert '..' */
              shift = 1;
              shift = 1;
              prev[1] = '^';
              prev[1] = '^';
            }
            }
          else
          else
            *cptr++ = '#';
            *cptr++ = '#';
          if (shift)
          if (shift)
            {
            {
              cptr = prev;
              cptr = prev;
              do
              do
                prev[0] = prev[shift];
                prev[0] = prev[shift];
              while (*prev++);
              while (*prev++);
            }
            }
        }
        }
    }
    }
 
 
  strcat (name, ".gcov");
  strcat (name, ".gcov");
  return name;
  return name;
}
}
 
 
/* 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)
          {
          {
            unsigned src_n = *++encoding;
            unsigned src_n = *++encoding;
 
 
            for (src = sources; src->index != src_n; src = src->next)
            for (src = sources; src->index != src_n; src = src->next)
              continue;
              continue;
            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;
            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 : &fn->src->lines[fn->line];
          line_t *block_line = line ? line : &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)" : "");
      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;
 
 
}
}
 
 
/* 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.  */
  char string[STRING_SIZE];     /* line buffer.  */
  char string[STRING_SIZE];     /* line buffer.  */
  char const *retval = "";      /* status of source file reading.  */
  char const *retval = "";      /* status of source file reading.  */
  function_t *fn = NULL;
  function_t *fn = NULL;
 
 
  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
  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,
  fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
           object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
           object_summary.ctrs[GCOV_COUNTER_ARCS].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, "%s:cannot open source file\n", src->name);
      fnotice (stderr, "%s:cannot open source file\n", src->name);
      retval = NULL;
      retval = NULL;
    }
    }
  else
  else
    {
    {
      struct stat status;
      struct stat status;
 
 
      if (!fstat (fileno (source_file), &status)
      if (!fstat (fileno (source_file), &status)
          && status.st_mtime > bbg_file_time)
          && status.st_mtime > bbg_file_time)
        {
        {
          fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
          fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
                   src->name, bbg_file_name);
                   src->name, bbg_file_name);
          fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
          fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
                   "-", 0);
                   "-", 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");
        }
        }
 
 
      /* 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 '#####' before the source line.  Otherwise,
         executed, print '#####' before the source line.  Otherwise,
         print the execution count before the source line.  There are
         print the execution count before the source line.  There are
         16 spaces of indentation added before the source line so that
         16 spaces of indentation added before the source line so that
         tabs won't be messed up.  */
         tabs won't be messed up.  */
      fprintf (gcov_file, "%9s:%5u:",
      fprintf (gcov_file, "%9s:%5u:",
               !line->exists ? "-" : !line->count ? "#####"
               !line->exists ? "-" : !line->count ? "#####"
               : format_gcov (line->count, 0, -1), line_num);
               : format_gcov (line->count, 0, -1), line_num);
 
 
      if (retval)
      if (retval)
        {
        {
          /* Copy source line.  */
          /* Copy source line.  */
          do
          do
            {
            {
              retval = fgets (string, STRING_SIZE, source_file);
              retval = fgets (string, STRING_SIZE, source_file);
              if (!retval)
              if (!retval)
                break;
                break;
              fputs (retval, gcov_file);
              fputs (retval, gcov_file);
            }
            }
          while (!retval[0] || retval[strlen (retval) - 1] != '\n');
          while (!retval[0] || retval[strlen (retval) - 1] != '\n');
        }
        }
      if (!retval)
      if (!retval)
        fputs ("/*EOF*/\n", gcov_file);
        fputs ("/*EOF*/\n", gcov_file);
 
 
      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),
                         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 = fgets (string, STRING_SIZE, source_file)); line_num++)
      for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
        {
        {
          fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
          fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
 
 
          while (!retval[0] || retval[strlen (retval) - 1] != '\n')
          while (!retval[0] || retval[strlen (retval) - 1] != '\n')
            {
            {
              retval = fgets (string, STRING_SIZE, source_file);
              retval = fgets (string, STRING_SIZE, source_file);
              if (!retval)
              if (!retval)
                break;
                break;
              fputs (retval, gcov_file);
              fputs (retval, gcov_file);
            }
            }
        }
        }
    }
    }
 
 
  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.