xref: /dflybsd-src/contrib/gcc-8.0/gcc/gcov.c (revision 95059079af47f9a66a175f374f2da1a5020e3255)
138fd1498Szrj /* Gcov.c: prepend line execution counts and branch probabilities to a
238fd1498Szrj    source file.
338fd1498Szrj    Copyright (C) 1990-2018 Free Software Foundation, Inc.
438fd1498Szrj    Contributed by James E. Wilson of Cygnus Support.
538fd1498Szrj    Mangled by Bob Manson of Cygnus Support.
638fd1498Szrj    Mangled further by Nathan Sidwell <nathan@codesourcery.com>
738fd1498Szrj 
838fd1498Szrj Gcov is free software; you can redistribute it and/or modify
938fd1498Szrj it under the terms of the GNU General Public License as published by
1038fd1498Szrj the Free Software Foundation; either version 3, or (at your option)
1138fd1498Szrj any later version.
1238fd1498Szrj 
1338fd1498Szrj Gcov is distributed in the hope that it will be useful,
1438fd1498Szrj but WITHOUT ANY WARRANTY; without even the implied warranty of
1538fd1498Szrj MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1638fd1498Szrj GNU General Public License for more details.
1738fd1498Szrj 
1838fd1498Szrj You should have received a copy of the GNU General Public License
1938fd1498Szrj along with Gcov; see the file COPYING3.  If not see
2038fd1498Szrj <http://www.gnu.org/licenses/>.  */
2138fd1498Szrj 
2238fd1498Szrj /* ??? Print a list of the ten blocks with the highest execution counts,
2338fd1498Szrj    and list the line numbers corresponding to those blocks.  Also, perhaps
2438fd1498Szrj    list the line numbers with the highest execution counts, only printing
2538fd1498Szrj    the first if there are several which are all listed in the same block.  */
2638fd1498Szrj 
2738fd1498Szrj /* ??? Should have an option to print the number of basic blocks, and the
2838fd1498Szrj    percent of them that are covered.  */
2938fd1498Szrj 
3038fd1498Szrj /* Need an option to show individual block counts, and show
3138fd1498Szrj    probabilities of fall through arcs.  */
3238fd1498Szrj 
3338fd1498Szrj #include "config.h"
3438fd1498Szrj #define INCLUDE_ALGORITHM
3538fd1498Szrj #define INCLUDE_VECTOR
3638fd1498Szrj #define INCLUDE_STRING
3738fd1498Szrj #define INCLUDE_MAP
3838fd1498Szrj #define INCLUDE_SET
3938fd1498Szrj #include "system.h"
4038fd1498Szrj #include "coretypes.h"
4138fd1498Szrj #include "tm.h"
4238fd1498Szrj #include "intl.h"
4338fd1498Szrj #include "diagnostic.h"
4438fd1498Szrj #include "version.h"
4538fd1498Szrj #include "demangle.h"
4638fd1498Szrj #include "color-macros.h"
4738fd1498Szrj 
4838fd1498Szrj #include <getopt.h>
4938fd1498Szrj 
5038fd1498Szrj #include "md5.h"
5138fd1498Szrj 
5238fd1498Szrj using namespace std;
5338fd1498Szrj 
5438fd1498Szrj #define IN_GCOV 1
5538fd1498Szrj #include "gcov-io.h"
5638fd1498Szrj #include "gcov-io.c"
5738fd1498Szrj 
5838fd1498Szrj /* The gcno file is generated by -ftest-coverage option. The gcda file is
5938fd1498Szrj    generated by a program compiled with -fprofile-arcs. Their formats
6038fd1498Szrj    are documented in gcov-io.h.  */
6138fd1498Szrj 
6238fd1498Szrj /* The functions in this file for creating and solution program flow graphs
6338fd1498Szrj    are very similar to functions in the gcc source file profile.c.  In
6438fd1498Szrj    some places we make use of the knowledge of how profile.c works to
6538fd1498Szrj    select particular algorithms here.  */
6638fd1498Szrj 
6738fd1498Szrj /* The code validates that the profile information read in corresponds
6838fd1498Szrj    to the code currently being compiled.  Rather than checking for
6938fd1498Szrj    identical files, the code below compares a checksum on the CFG
7038fd1498Szrj    (based on the order of basic blocks and the arcs in the CFG).  If
7138fd1498Szrj    the CFG checksum in the gcda file match the CFG checksum in the
7238fd1498Szrj    gcno file, the profile data will be used.  */
7338fd1498Szrj 
7438fd1498Szrj /* This is the size of the buffer used to read in source file lines.  */
7538fd1498Szrj 
7638fd1498Szrj struct function_info;
7738fd1498Szrj struct block_info;
7838fd1498Szrj struct source_info;
7938fd1498Szrj 
8038fd1498Szrj /* Describes an arc between two basic blocks.  */
8138fd1498Szrj 
8238fd1498Szrj struct arc_info
8338fd1498Szrj {
8438fd1498Szrj   /* source and destination blocks.  */
8538fd1498Szrj   struct block_info *src;
8638fd1498Szrj   struct block_info *dst;
8738fd1498Szrj 
8838fd1498Szrj   /* transition counts.  */
8938fd1498Szrj   gcov_type count;
9038fd1498Szrj   /* used in cycle search, so that we do not clobber original counts.  */
9138fd1498Szrj   gcov_type cs_count;
9238fd1498Szrj 
9338fd1498Szrj   unsigned int count_valid : 1;
9438fd1498Szrj   unsigned int on_tree : 1;
9538fd1498Szrj   unsigned int fake : 1;
9638fd1498Szrj   unsigned int fall_through : 1;
9738fd1498Szrj 
9838fd1498Szrj   /* Arc to a catch handler.  */
9938fd1498Szrj   unsigned int is_throw : 1;
10038fd1498Szrj 
10138fd1498Szrj   /* Arc is for a function that abnormally returns.  */
10238fd1498Szrj   unsigned int is_call_non_return : 1;
10338fd1498Szrj 
10438fd1498Szrj   /* Arc is for catch/setjmp.  */
10538fd1498Szrj   unsigned int is_nonlocal_return : 1;
10638fd1498Szrj 
10738fd1498Szrj   /* Is an unconditional branch.  */
10838fd1498Szrj   unsigned int is_unconditional : 1;
10938fd1498Szrj 
11038fd1498Szrj   /* Loop making arc.  */
11138fd1498Szrj   unsigned int cycle : 1;
11238fd1498Szrj 
11338fd1498Szrj   /* Links to next arc on src and dst lists.  */
11438fd1498Szrj   struct arc_info *succ_next;
11538fd1498Szrj   struct arc_info *pred_next;
11638fd1498Szrj };
11738fd1498Szrj 
11838fd1498Szrj /* Describes which locations (lines and files) are associated with
11938fd1498Szrj    a basic block.  */
12038fd1498Szrj 
12138fd1498Szrj struct block_location_info
12238fd1498Szrj {
block_location_infoblock_location_info12338fd1498Szrj   block_location_info (unsigned _source_file_idx):
12438fd1498Szrj     source_file_idx (_source_file_idx)
12538fd1498Szrj   {}
12638fd1498Szrj 
12738fd1498Szrj   unsigned source_file_idx;
12838fd1498Szrj   vector<unsigned> lines;
12938fd1498Szrj };
13038fd1498Szrj 
13138fd1498Szrj /* Describes a basic block. Contains lists of arcs to successor and
13238fd1498Szrj    predecessor blocks.  */
13338fd1498Szrj 
13438fd1498Szrj struct block_info
13538fd1498Szrj {
13638fd1498Szrj   /* Constructor.  */
13738fd1498Szrj   block_info ();
13838fd1498Szrj 
13938fd1498Szrj   /* Chain of exit and entry arcs.  */
14038fd1498Szrj   arc_info *succ;
14138fd1498Szrj   arc_info *pred;
14238fd1498Szrj 
14338fd1498Szrj   /* Number of unprocessed exit and entry arcs.  */
14438fd1498Szrj   gcov_type num_succ;
14538fd1498Szrj   gcov_type num_pred;
14638fd1498Szrj 
14738fd1498Szrj   unsigned id;
14838fd1498Szrj 
14938fd1498Szrj   /* Block execution count.  */
15038fd1498Szrj   gcov_type count;
15138fd1498Szrj   unsigned count_valid : 1;
15238fd1498Szrj   unsigned valid_chain : 1;
15338fd1498Szrj   unsigned invalid_chain : 1;
15438fd1498Szrj   unsigned exceptional : 1;
15538fd1498Szrj 
15638fd1498Szrj   /* Block is a call instrumenting site.  */
15738fd1498Szrj   unsigned is_call_site : 1; /* Does the call.  */
15838fd1498Szrj   unsigned is_call_return : 1; /* Is the return.  */
15938fd1498Szrj 
16038fd1498Szrj   /* Block is a landing pad for longjmp or throw.  */
16138fd1498Szrj   unsigned is_nonlocal_return : 1;
16238fd1498Szrj 
16338fd1498Szrj   vector<block_location_info> locations;
16438fd1498Szrj 
16538fd1498Szrj   struct
16638fd1498Szrj   {
16738fd1498Szrj     /* Single line graph cycle workspace.  Used for all-blocks
16838fd1498Szrj        mode.  */
16938fd1498Szrj     arc_info *arc;
17038fd1498Szrj     unsigned ident;
17138fd1498Szrj   } cycle; /* Used in all-blocks mode, after blocks are linked onto
17238fd1498Szrj 	     lines.  */
17338fd1498Szrj 
17438fd1498Szrj   /* Temporary chain for solving graph, and for chaining blocks on one
17538fd1498Szrj      line.  */
17638fd1498Szrj   struct block_info *chain;
17738fd1498Szrj 
17838fd1498Szrj };
17938fd1498Szrj 
block_info()18038fd1498Szrj block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
18138fd1498Szrj   id (0), count (0), count_valid (0), valid_chain (0), invalid_chain (0),
18238fd1498Szrj   exceptional (0), is_call_site (0), is_call_return (0), is_nonlocal_return (0),
18338fd1498Szrj   locations (), chain (NULL)
18438fd1498Szrj {
18538fd1498Szrj   cycle.arc = NULL;
18638fd1498Szrj }
18738fd1498Szrj 
18838fd1498Szrj /* Describes a single line of source.  Contains a chain of basic blocks
18938fd1498Szrj    with code on it.  */
19038fd1498Szrj 
19138fd1498Szrj struct line_info
19238fd1498Szrj {
19338fd1498Szrj   /* Default constructor.  */
19438fd1498Szrj   line_info ();
19538fd1498Szrj 
19638fd1498Szrj   /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
19738fd1498Szrj   bool has_block (block_info *needle);
19838fd1498Szrj 
19938fd1498Szrj   /* Execution count.  */
20038fd1498Szrj   gcov_type count;
20138fd1498Szrj 
20238fd1498Szrj   /* Branches from blocks that end on this line.  */
20338fd1498Szrj   vector<arc_info *> branches;
20438fd1498Szrj 
20538fd1498Szrj   /* blocks which start on this line.  Used in all-blocks mode.  */
20638fd1498Szrj   vector<block_info *> blocks;
20738fd1498Szrj 
20838fd1498Szrj   unsigned exists : 1;
20938fd1498Szrj   unsigned unexceptional : 1;
21038fd1498Szrj   unsigned has_unexecuted_block : 1;
21138fd1498Szrj };
21238fd1498Szrj 
line_info()21338fd1498Szrj line_info::line_info (): count (0), branches (), blocks (), exists (false),
21438fd1498Szrj   unexceptional (0), has_unexecuted_block (0)
21538fd1498Szrj {
21638fd1498Szrj }
21738fd1498Szrj 
21838fd1498Szrj bool
has_block(block_info * needle)21938fd1498Szrj line_info::has_block (block_info *needle)
22038fd1498Szrj {
22138fd1498Szrj   return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
22238fd1498Szrj }
22338fd1498Szrj 
22438fd1498Szrj /* Describes a single function. Contains an array of basic blocks.  */
22538fd1498Szrj 
22638fd1498Szrj struct function_info
22738fd1498Szrj {
22838fd1498Szrj   function_info ();
22938fd1498Szrj   ~function_info ();
23038fd1498Szrj 
23138fd1498Szrj   /* Return true when line N belongs to the function in source file SRC_IDX.
23238fd1498Szrj      The line must be defined in body of the function, can't be inlined.  */
23338fd1498Szrj   bool group_line_p (unsigned n, unsigned src_idx);
23438fd1498Szrj 
23538fd1498Szrj   /* Function filter based on function_info::artificial variable.  */
23638fd1498Szrj 
23738fd1498Szrj   static inline bool
is_artificialfunction_info23838fd1498Szrj   is_artificial (function_info *fn)
23938fd1498Szrj   {
24038fd1498Szrj     return fn->artificial;
24138fd1498Szrj   }
24238fd1498Szrj 
24338fd1498Szrj   /* Name of function.  */
24438fd1498Szrj   char *name;
24538fd1498Szrj   char *demangled_name;
24638fd1498Szrj   unsigned ident;
24738fd1498Szrj   unsigned lineno_checksum;
24838fd1498Szrj   unsigned cfg_checksum;
24938fd1498Szrj 
25038fd1498Szrj   /* The graph contains at least one fake incoming edge.  */
25138fd1498Szrj   unsigned has_catch : 1;
25238fd1498Szrj 
25338fd1498Szrj   /* True when the function is artificial and does not exist
25438fd1498Szrj      in a source file.  */
25538fd1498Szrj   unsigned artificial : 1;
25638fd1498Szrj 
25738fd1498Szrj   /* True when multiple functions start at a line in a source file.  */
25838fd1498Szrj   unsigned is_group : 1;
25938fd1498Szrj 
26038fd1498Szrj   /* Array of basic blocks.  Like in GCC, the entry block is
26138fd1498Szrj      at blocks[0] and the exit block is at blocks[1].  */
26238fd1498Szrj #define ENTRY_BLOCK (0)
26338fd1498Szrj #define EXIT_BLOCK (1)
26438fd1498Szrj   vector<block_info> blocks;
26538fd1498Szrj   unsigned blocks_executed;
26638fd1498Szrj 
26738fd1498Szrj   /* Raw arc coverage counts.  */
26838fd1498Szrj   vector<gcov_type> counts;
26938fd1498Szrj 
27038fd1498Szrj   /* First line number.  */
27138fd1498Szrj   unsigned start_line;
27238fd1498Szrj 
27338fd1498Szrj   /* First line column.  */
27438fd1498Szrj   unsigned start_column;
27538fd1498Szrj 
27638fd1498Szrj   /* Last line number.  */
27738fd1498Szrj   unsigned end_line;
27838fd1498Szrj 
27938fd1498Szrj   /* Index of source file where the function is defined.  */
28038fd1498Szrj   unsigned src;
28138fd1498Szrj 
28238fd1498Szrj   /* Vector of line information.  */
28338fd1498Szrj   vector<line_info> lines;
28438fd1498Szrj 
28538fd1498Szrj   /* Next function.  */
28638fd1498Szrj   struct function_info *next;
28738fd1498Szrj };
28838fd1498Szrj 
28938fd1498Szrj /* Function info comparer that will sort functions according to starting
29038fd1498Szrj    line.  */
29138fd1498Szrj 
29238fd1498Szrj struct function_line_start_cmp
29338fd1498Szrj {
operatorfunction_line_start_cmp29438fd1498Szrj   inline bool operator() (const function_info *lhs,
29538fd1498Szrj 			  const function_info *rhs)
29638fd1498Szrj     {
29738fd1498Szrj       return (lhs->start_line == rhs->start_line
29838fd1498Szrj 	      ? lhs->start_column < rhs->start_column
29938fd1498Szrj 	      : lhs->start_line < rhs->start_line);
30038fd1498Szrj     }
30138fd1498Szrj };
30238fd1498Szrj 
30338fd1498Szrj /* Describes coverage of a file or function.  */
30438fd1498Szrj 
30538fd1498Szrj struct coverage_info
30638fd1498Szrj {
30738fd1498Szrj   int lines;
30838fd1498Szrj   int lines_executed;
30938fd1498Szrj 
31038fd1498Szrj   int branches;
31138fd1498Szrj   int branches_executed;
31238fd1498Szrj   int branches_taken;
31338fd1498Szrj 
31438fd1498Szrj   int calls;
31538fd1498Szrj   int calls_executed;
31638fd1498Szrj 
31738fd1498Szrj   char *name;
31838fd1498Szrj };
31938fd1498Szrj 
32038fd1498Szrj /* Describes a file mentioned in the block graph.  Contains an array
32138fd1498Szrj    of line info.  */
32238fd1498Szrj 
32338fd1498Szrj struct source_info
32438fd1498Szrj {
32538fd1498Szrj   /* Default constructor.  */
32638fd1498Szrj   source_info ();
32738fd1498Szrj 
32838fd1498Szrj   vector<function_info *> get_functions_at_location (unsigned line_num) const;
32938fd1498Szrj 
33038fd1498Szrj   /* Index of the source_info in sources vector.  */
33138fd1498Szrj   unsigned index;
33238fd1498Szrj 
33338fd1498Szrj   /* Canonical name of source file.  */
33438fd1498Szrj   char *name;
33538fd1498Szrj   time_t file_time;
33638fd1498Szrj 
33738fd1498Szrj   /* Vector of line information.  */
33838fd1498Szrj   vector<line_info> lines;
33938fd1498Szrj 
34038fd1498Szrj   coverage_info coverage;
34138fd1498Szrj 
34238fd1498Szrj   /* Functions in this source file.  These are in ascending line
34338fd1498Szrj      number order.  */
34438fd1498Szrj   vector <function_info *> functions;
34538fd1498Szrj };
34638fd1498Szrj 
source_info()34738fd1498Szrj source_info::source_info (): index (0), name (NULL), file_time (),
34838fd1498Szrj   lines (), coverage (), functions ()
34938fd1498Szrj {
35038fd1498Szrj }
35138fd1498Szrj 
35238fd1498Szrj vector<function_info *>
get_functions_at_location(unsigned line_num)35338fd1498Szrj source_info::get_functions_at_location (unsigned line_num) const
35438fd1498Szrj {
35538fd1498Szrj   vector<function_info *> r;
35638fd1498Szrj 
35738fd1498Szrj   for (vector<function_info *>::const_iterator it = functions.begin ();
35838fd1498Szrj        it != functions.end (); it++)
35938fd1498Szrj     {
36038fd1498Szrj       if ((*it)->start_line == line_num && (*it)->src == index)
36138fd1498Szrj 	r.push_back (*it);
36238fd1498Szrj     }
36338fd1498Szrj 
36438fd1498Szrj   std::sort (r.begin (), r.end (), function_line_start_cmp ());
36538fd1498Szrj 
36638fd1498Szrj   return r;
36738fd1498Szrj }
36838fd1498Szrj 
36938fd1498Szrj class name_map
37038fd1498Szrj {
37138fd1498Szrj public:
name_map()37238fd1498Szrj   name_map ()
37338fd1498Szrj   {
37438fd1498Szrj   }
37538fd1498Szrj 
name_map(char * _name,unsigned _src)37638fd1498Szrj   name_map (char *_name, unsigned _src): name (_name), src (_src)
37738fd1498Szrj   {
37838fd1498Szrj   }
37938fd1498Szrj 
38038fd1498Szrj   bool operator== (const name_map &rhs) const
38138fd1498Szrj   {
38238fd1498Szrj #if HAVE_DOS_BASED_FILE_SYSTEM
38338fd1498Szrj     return strcasecmp (this->name, rhs.name) == 0;
38438fd1498Szrj #else
38538fd1498Szrj     return strcmp (this->name, rhs.name) == 0;
38638fd1498Szrj #endif
38738fd1498Szrj   }
38838fd1498Szrj 
38938fd1498Szrj   bool operator< (const name_map &rhs) const
39038fd1498Szrj   {
39138fd1498Szrj #if HAVE_DOS_BASED_FILE_SYSTEM
39238fd1498Szrj     return strcasecmp (this->name, rhs.name) < 0;
39338fd1498Szrj #else
39438fd1498Szrj     return strcmp (this->name, rhs.name) < 0;
39538fd1498Szrj #endif
39638fd1498Szrj   }
39738fd1498Szrj 
39838fd1498Szrj   const char *name;  /* Source file name */
39938fd1498Szrj   unsigned src;  /* Source file */
40038fd1498Szrj };
40138fd1498Szrj 
40238fd1498Szrj /* Vector of all functions.  */
40338fd1498Szrj static vector<function_info *> functions;
40438fd1498Szrj 
40538fd1498Szrj /* Vector of source files.  */
40638fd1498Szrj static vector<source_info> sources;
40738fd1498Szrj 
40838fd1498Szrj /* Mapping of file names to sources */
40938fd1498Szrj static vector<name_map> names;
41038fd1498Szrj 
41138fd1498Szrj /* This holds data summary information.  */
41238fd1498Szrj 
41338fd1498Szrj static unsigned object_runs;
41438fd1498Szrj static unsigned program_count;
41538fd1498Szrj 
41638fd1498Szrj static unsigned total_lines;
41738fd1498Szrj static unsigned total_executed;
41838fd1498Szrj 
41938fd1498Szrj /* Modification time of graph file.  */
42038fd1498Szrj 
42138fd1498Szrj static time_t bbg_file_time;
42238fd1498Szrj 
42338fd1498Szrj /* Name of the notes (gcno) output file.  The "bbg" prefix is for
42438fd1498Szrj    historical reasons, when the notes file contained only the
42538fd1498Szrj    basic block graph notes.  */
42638fd1498Szrj 
42738fd1498Szrj static char *bbg_file_name;
42838fd1498Szrj 
42938fd1498Szrj /* Stamp of the bbg file */
43038fd1498Szrj static unsigned bbg_stamp;
43138fd1498Szrj 
43238fd1498Szrj /* Supports has_unexecuted_blocks functionality.  */
43338fd1498Szrj static unsigned bbg_supports_has_unexecuted_blocks;
43438fd1498Szrj 
43538fd1498Szrj /* Name and file pointer of the input file for the count data (gcda).  */
43638fd1498Szrj 
43738fd1498Szrj static char *da_file_name;
43838fd1498Szrj 
43938fd1498Szrj /* Data file is missing.  */
44038fd1498Szrj 
44138fd1498Szrj static int no_data_file;
44238fd1498Szrj 
44338fd1498Szrj /* If there is several input files, compute and display results after
44438fd1498Szrj    reading all data files.  This way if two or more gcda file refer to
44538fd1498Szrj    the same source file (eg inline subprograms in a .h file), the
44638fd1498Szrj    counts are added.  */
44738fd1498Szrj 
44838fd1498Szrj static int multiple_files = 0;
44938fd1498Szrj 
45038fd1498Szrj /* Output branch probabilities.  */
45138fd1498Szrj 
45238fd1498Szrj static int flag_branches = 0;
45338fd1498Szrj 
45438fd1498Szrj /* Show unconditional branches too.  */
45538fd1498Szrj static int flag_unconditional = 0;
45638fd1498Szrj 
45738fd1498Szrj /* Output a gcov file if this is true.  This is on by default, and can
45838fd1498Szrj    be turned off by the -n option.  */
45938fd1498Szrj 
46038fd1498Szrj static int flag_gcov_file = 1;
46138fd1498Szrj 
46238fd1498Szrj /* Output progress indication if this is true.  This is off by default
46338fd1498Szrj    and can be turned on by the -d option.  */
46438fd1498Szrj 
46538fd1498Szrj static int flag_display_progress = 0;
46638fd1498Szrj 
46738fd1498Szrj /* Output *.gcov file in intermediate format used by 'lcov'.  */
46838fd1498Szrj 
46938fd1498Szrj static int flag_intermediate_format = 0;
47038fd1498Szrj 
47138fd1498Szrj /* Output demangled function names.  */
47238fd1498Szrj 
47338fd1498Szrj static int flag_demangled_names = 0;
47438fd1498Szrj 
47538fd1498Szrj /* For included files, make the gcov output file name include the name
47638fd1498Szrj    of the input source file.  For example, if x.h is included in a.c,
47738fd1498Szrj    then the output file name is a.c##x.h.gcov instead of x.h.gcov.  */
47838fd1498Szrj 
47938fd1498Szrj static int flag_long_names = 0;
48038fd1498Szrj 
48138fd1498Szrj /* For situations when a long name can potentially hit filesystem path limit,
48238fd1498Szrj    let's calculate md5sum of the path and append it to a file name.  */
48338fd1498Szrj 
48438fd1498Szrj static int flag_hash_filenames = 0;
48538fd1498Szrj 
48638fd1498Szrj /* Print verbose informations.  */
48738fd1498Szrj 
48838fd1498Szrj static int flag_verbose = 0;
48938fd1498Szrj 
49038fd1498Szrj /* Print colored output.  */
49138fd1498Szrj 
49238fd1498Szrj static int flag_use_colors = 0;
49338fd1498Szrj 
49438fd1498Szrj /* Output count information for every basic block, not merely those
49538fd1498Szrj    that contain line number information.  */
49638fd1498Szrj 
49738fd1498Szrj static int flag_all_blocks = 0;
49838fd1498Szrj 
49938fd1498Szrj /* Output human readable numbers.  */
50038fd1498Szrj 
50138fd1498Szrj static int flag_human_readable_numbers = 0;
50238fd1498Szrj 
50338fd1498Szrj /* Output summary info for each function.  */
50438fd1498Szrj 
50538fd1498Szrj static int flag_function_summary = 0;
50638fd1498Szrj 
50738fd1498Szrj /* Object directory file prefix.  This is the directory/file where the
50838fd1498Szrj    graph and data files are looked for, if nonzero.  */
50938fd1498Szrj 
51038fd1498Szrj static char *object_directory = 0;
51138fd1498Szrj 
51238fd1498Szrj /* Source directory prefix.  This is removed from source pathnames
51338fd1498Szrj    that match, when generating the output file name.  */
51438fd1498Szrj 
51538fd1498Szrj static char *source_prefix = 0;
51638fd1498Szrj static size_t source_length = 0;
51738fd1498Szrj 
51838fd1498Szrj /* Only show data for sources with relative pathnames.  Absolute ones
51938fd1498Szrj    usually indicate a system header file, which although it may
52038fd1498Szrj    contain inline functions, is usually uninteresting.  */
52138fd1498Szrj static int flag_relative_only = 0;
52238fd1498Szrj 
52338fd1498Szrj /* Preserve all pathname components. Needed when object files and
52438fd1498Szrj    source files are in subdirectories. '/' is mangled as '#', '.' is
52538fd1498Szrj    elided and '..' mangled to '^'.  */
52638fd1498Szrj 
52738fd1498Szrj static int flag_preserve_paths = 0;
52838fd1498Szrj 
52938fd1498Szrj /* Output the number of times a branch was taken as opposed to the percentage
53038fd1498Szrj    of times it was taken.  */
53138fd1498Szrj 
53238fd1498Szrj static int flag_counts = 0;
53338fd1498Szrj 
53438fd1498Szrj /* Forward declarations.  */
53538fd1498Szrj static int process_args (int, char **);
53638fd1498Szrj static void print_usage (int) ATTRIBUTE_NORETURN;
53738fd1498Szrj static void print_version (void) ATTRIBUTE_NORETURN;
53838fd1498Szrj static void process_file (const char *);
539*58e805e6Szrj static void process_all_functions (void);
54038fd1498Szrj static void generate_results (const char *);
54138fd1498Szrj static void create_file_names (const char *);
54238fd1498Szrj static char *canonicalize_name (const char *);
54338fd1498Szrj static unsigned find_source (const char *);
54438fd1498Szrj static void read_graph_file (void);
54538fd1498Szrj static int read_count_file (void);
54638fd1498Szrj static void solve_flow_graph (function_info *);
54738fd1498Szrj static void find_exception_blocks (function_info *);
54838fd1498Szrj static void add_branch_counts (coverage_info *, const arc_info *);
54938fd1498Szrj static void add_line_counts (coverage_info *, function_info *);
55038fd1498Szrj static void executed_summary (unsigned, unsigned);
55138fd1498Szrj static void function_summary (const coverage_info *, const char *);
55238fd1498Szrj static const char *format_gcov (gcov_type, gcov_type, int);
55338fd1498Szrj static void accumulate_line_counts (source_info *);
55438fd1498Szrj static void output_gcov_file (const char *, source_info *);
55538fd1498Szrj static int output_branch_count (FILE *, int, const arc_info *);
55638fd1498Szrj static void output_lines (FILE *, const source_info *);
55738fd1498Szrj static char *make_gcov_file_name (const char *, const char *);
55838fd1498Szrj static char *mangle_name (const char *, char *);
55938fd1498Szrj static void release_structures (void);
56038fd1498Szrj extern int main (int, char **);
56138fd1498Szrj 
function_info()56238fd1498Szrj function_info::function_info (): name (NULL), demangled_name (NULL),
56338fd1498Szrj   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
56438fd1498Szrj   artificial (0), is_group (0),
56538fd1498Szrj   blocks (), blocks_executed (0), counts (),
56638fd1498Szrj   start_line (0), start_column (), end_line (0), src (0), lines (), next (NULL)
56738fd1498Szrj {
56838fd1498Szrj }
56938fd1498Szrj 
~function_info()57038fd1498Szrj function_info::~function_info ()
57138fd1498Szrj {
57238fd1498Szrj   for (int i = blocks.size () - 1; i >= 0; i--)
57338fd1498Szrj     {
57438fd1498Szrj       arc_info *arc, *arc_n;
57538fd1498Szrj 
57638fd1498Szrj       for (arc = blocks[i].succ; arc; arc = arc_n)
57738fd1498Szrj 	{
57838fd1498Szrj 	  arc_n = arc->succ_next;
57938fd1498Szrj 	  free (arc);
58038fd1498Szrj 	}
58138fd1498Szrj     }
58238fd1498Szrj   if (flag_demangled_names && demangled_name != name)
58338fd1498Szrj     free (demangled_name);
58438fd1498Szrj   free (name);
58538fd1498Szrj }
58638fd1498Szrj 
group_line_p(unsigned n,unsigned src_idx)58738fd1498Szrj bool function_info::group_line_p (unsigned n, unsigned src_idx)
58838fd1498Szrj {
58938fd1498Szrj   return is_group && src == src_idx && start_line <= n && n <= end_line;
59038fd1498Szrj }
59138fd1498Szrj 
59238fd1498Szrj /* Cycle detection!
59338fd1498Szrj    There are a bajillion algorithms that do this.  Boost's function is named
59438fd1498Szrj    hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
59538fd1498Szrj    "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs"
59638fd1498Szrj    (url at <http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf>).
59738fd1498Szrj 
59838fd1498Szrj    The basic algorithm is simple: effectively, we're finding all simple paths
59938fd1498Szrj    in a subgraph (that shrinks every iteration).  Duplicates are filtered by
60038fd1498Szrj    "blocking" a path when a node is added to the path (this also prevents non-
60138fd1498Szrj    simple paths)--the node is unblocked only when it participates in a cycle.
60238fd1498Szrj    */
60338fd1498Szrj 
60438fd1498Szrj typedef vector<arc_info *> arc_vector_t;
60538fd1498Szrj typedef vector<const block_info *> block_vector_t;
60638fd1498Szrj 
60738fd1498Szrj /* Enum with types of loop in CFG.  */
60838fd1498Szrj 
60938fd1498Szrj enum loop_type
61038fd1498Szrj {
61138fd1498Szrj   NO_LOOP = 0,
61238fd1498Szrj   LOOP = 1,
61338fd1498Szrj   NEGATIVE_LOOP = 3
61438fd1498Szrj };
61538fd1498Szrj 
61638fd1498Szrj /* Loop_type operator that merges two values: A and B.  */
61738fd1498Szrj 
61838fd1498Szrj inline loop_type& operator |= (loop_type& a, loop_type b)
61938fd1498Szrj {
62038fd1498Szrj     return a = static_cast<loop_type> (a | b);
62138fd1498Szrj }
62238fd1498Szrj 
62338fd1498Szrj /* Handle cycle identified by EDGES, where the function finds minimum cs_count
62438fd1498Szrj    and subtract the value from all counts.  The subtracted value is added
62538fd1498Szrj    to COUNT.  Returns type of loop.  */
62638fd1498Szrj 
62738fd1498Szrj static loop_type
handle_cycle(const arc_vector_t & edges,int64_t & count)62838fd1498Szrj handle_cycle (const arc_vector_t &edges, int64_t &count)
62938fd1498Szrj {
63038fd1498Szrj   /* Find the minimum edge of the cycle, and reduce all nodes in the cycle by
63138fd1498Szrj      that amount.  */
63238fd1498Szrj   int64_t cycle_count = INTTYPE_MAXIMUM (int64_t);
63338fd1498Szrj   for (unsigned i = 0; i < edges.size (); i++)
63438fd1498Szrj     {
63538fd1498Szrj       int64_t ecount = edges[i]->cs_count;
63638fd1498Szrj       if (cycle_count > ecount)
63738fd1498Szrj 	cycle_count = ecount;
63838fd1498Szrj     }
63938fd1498Szrj   count += cycle_count;
64038fd1498Szrj   for (unsigned i = 0; i < edges.size (); i++)
64138fd1498Szrj     edges[i]->cs_count -= cycle_count;
64238fd1498Szrj 
64338fd1498Szrj   return cycle_count < 0 ? NEGATIVE_LOOP : LOOP;
64438fd1498Szrj }
64538fd1498Szrj 
64638fd1498Szrj /* Unblock a block U from BLOCKED.  Apart from that, iterate all blocks
64738fd1498Szrj    blocked by U in BLOCK_LISTS.  */
64838fd1498Szrj 
64938fd1498Szrj static void
unblock(const block_info * u,block_vector_t & blocked,vector<block_vector_t> & block_lists)65038fd1498Szrj unblock (const block_info *u, block_vector_t &blocked,
65138fd1498Szrj 	 vector<block_vector_t > &block_lists)
65238fd1498Szrj {
65338fd1498Szrj   block_vector_t::iterator it = find (blocked.begin (), blocked.end (), u);
65438fd1498Szrj   if (it == blocked.end ())
65538fd1498Szrj     return;
65638fd1498Szrj 
65738fd1498Szrj   unsigned index = it - blocked.begin ();
65838fd1498Szrj   blocked.erase (it);
65938fd1498Szrj 
66038fd1498Szrj   block_vector_t to_unblock (block_lists[index]);
66138fd1498Szrj 
66238fd1498Szrj   block_lists.erase (block_lists.begin () + index);
66338fd1498Szrj 
66438fd1498Szrj   for (block_vector_t::iterator it = to_unblock.begin ();
66538fd1498Szrj        it != to_unblock.end (); it++)
66638fd1498Szrj     unblock (*it, blocked, block_lists);
66738fd1498Szrj }
66838fd1498Szrj 
66938fd1498Szrj /* Find circuit going to block V, PATH is provisional seen cycle.
67038fd1498Szrj    BLOCKED is vector of blocked vertices, BLOCK_LISTS contains vertices
67138fd1498Szrj    blocked by a block.  COUNT is accumulated count of the current LINE.
67238fd1498Szrj    Returns what type of loop it contains.  */
67338fd1498Szrj 
67438fd1498Szrj static loop_type
circuit(block_info * v,arc_vector_t & path,block_info * start,block_vector_t & blocked,vector<block_vector_t> & block_lists,line_info & linfo,int64_t & count)67538fd1498Szrj circuit (block_info *v, arc_vector_t &path, block_info *start,
67638fd1498Szrj 	 block_vector_t &blocked, vector<block_vector_t> &block_lists,
67738fd1498Szrj 	 line_info &linfo, int64_t &count)
67838fd1498Szrj {
67938fd1498Szrj   loop_type result = NO_LOOP;
68038fd1498Szrj 
68138fd1498Szrj   /* Add v to the block list.  */
68238fd1498Szrj   gcc_assert (find (blocked.begin (), blocked.end (), v) == blocked.end ());
68338fd1498Szrj   blocked.push_back (v);
68438fd1498Szrj   block_lists.push_back (block_vector_t ());
68538fd1498Szrj 
68638fd1498Szrj   for (arc_info *arc = v->succ; arc; arc = arc->succ_next)
68738fd1498Szrj     {
68838fd1498Szrj       block_info *w = arc->dst;
68938fd1498Szrj       if (w < start || !linfo.has_block (w))
69038fd1498Szrj 	continue;
69138fd1498Szrj 
69238fd1498Szrj       path.push_back (arc);
69338fd1498Szrj       if (w == start)
69438fd1498Szrj 	/* Cycle has been found.  */
69538fd1498Szrj 	result |= handle_cycle (path, count);
69638fd1498Szrj       else if (find (blocked.begin (), blocked.end (), w) == blocked.end ())
69738fd1498Szrj 	result |= circuit (w, path, start, blocked, block_lists, linfo, count);
69838fd1498Szrj 
69938fd1498Szrj       path.pop_back ();
70038fd1498Szrj     }
70138fd1498Szrj 
70238fd1498Szrj   if (result != NO_LOOP)
70338fd1498Szrj     unblock (v, blocked, block_lists);
70438fd1498Szrj   else
70538fd1498Szrj     for (arc_info *arc = v->succ; arc; arc = arc->succ_next)
70638fd1498Szrj       {
70738fd1498Szrj 	block_info *w = arc->dst;
70838fd1498Szrj 	if (w < start || !linfo.has_block (w))
70938fd1498Szrj 	  continue;
71038fd1498Szrj 
71138fd1498Szrj 	size_t index
71238fd1498Szrj 	  = find (blocked.begin (), blocked.end (), w) - blocked.begin ();
71338fd1498Szrj 	gcc_assert (index < blocked.size ());
71438fd1498Szrj 	block_vector_t &list = block_lists[index];
71538fd1498Szrj 	if (find (list.begin (), list.end (), v) == list.end ())
71638fd1498Szrj 	  list.push_back (v);
71738fd1498Szrj       }
71838fd1498Szrj 
71938fd1498Szrj   return result;
72038fd1498Szrj }
72138fd1498Szrj 
72238fd1498Szrj /* Find cycles for a LINFO.  If HANDLE_NEGATIVE_CYCLES is set and the line
72338fd1498Szrj    contains a negative loop, then perform the same function once again.  */
72438fd1498Szrj 
72538fd1498Szrj static gcov_type
72638fd1498Szrj get_cycles_count (line_info &linfo, bool handle_negative_cycles = true)
72738fd1498Szrj {
72838fd1498Szrj   /* Note that this algorithm works even if blocks aren't in sorted order.
72938fd1498Szrj      Each iteration of the circuit detection is completely independent
73038fd1498Szrj      (except for reducing counts, but that shouldn't matter anyways).
73138fd1498Szrj      Therefore, operating on a permuted order (i.e., non-sorted) only
73238fd1498Szrj      has the effect of permuting the output cycles.  */
73338fd1498Szrj 
73438fd1498Szrj   loop_type result = NO_LOOP;
73538fd1498Szrj   gcov_type count = 0;
73638fd1498Szrj   for (vector<block_info *>::iterator it = linfo.blocks.begin ();
73738fd1498Szrj        it != linfo.blocks.end (); it++)
73838fd1498Szrj     {
73938fd1498Szrj       arc_vector_t path;
74038fd1498Szrj       block_vector_t blocked;
74138fd1498Szrj       vector<block_vector_t > block_lists;
74238fd1498Szrj       result |= circuit (*it, path, *it, blocked, block_lists, linfo,
74338fd1498Szrj 			 count);
74438fd1498Szrj     }
74538fd1498Szrj 
74638fd1498Szrj   /* If we have a negative cycle, repeat the find_cycles routine.  */
74738fd1498Szrj   if (result == NEGATIVE_LOOP && handle_negative_cycles)
74838fd1498Szrj     count += get_cycles_count (linfo, false);
74938fd1498Szrj 
75038fd1498Szrj   return count;
75138fd1498Szrj }
75238fd1498Szrj 
75338fd1498Szrj int
main(int argc,char ** argv)75438fd1498Szrj main (int argc, char **argv)
75538fd1498Szrj {
75638fd1498Szrj   int argno;
75738fd1498Szrj   int first_arg;
75838fd1498Szrj   const char *p;
75938fd1498Szrj 
76038fd1498Szrj   p = argv[0] + strlen (argv[0]);
76138fd1498Szrj   while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
76238fd1498Szrj     --p;
76338fd1498Szrj   progname = p;
76438fd1498Szrj 
76538fd1498Szrj   xmalloc_set_program_name (progname);
76638fd1498Szrj 
76738fd1498Szrj   /* Unlock the stdio streams.  */
76838fd1498Szrj   unlock_std_streams ();
76938fd1498Szrj 
77038fd1498Szrj   gcc_init_libintl ();
77138fd1498Szrj 
77238fd1498Szrj   diagnostic_initialize (global_dc, 0);
77338fd1498Szrj 
77438fd1498Szrj   /* Handle response files.  */
77538fd1498Szrj   expandargv (&argc, &argv);
77638fd1498Szrj 
77738fd1498Szrj   argno = process_args (argc, argv);
77838fd1498Szrj   if (optind == argc)
77938fd1498Szrj     print_usage (true);
78038fd1498Szrj 
78138fd1498Szrj   if (argc - argno > 1)
78238fd1498Szrj     multiple_files = 1;
78338fd1498Szrj 
78438fd1498Szrj   first_arg = argno;
78538fd1498Szrj 
78638fd1498Szrj   for (; argno != argc; argno++)
78738fd1498Szrj     {
78838fd1498Szrj       if (flag_display_progress)
78938fd1498Szrj 	printf ("Processing file %d out of %d\n", argno - first_arg + 1,
79038fd1498Szrj 		argc - first_arg);
79138fd1498Szrj       process_file (argv[argno]);
79238fd1498Szrj 
79338fd1498Szrj       if (flag_intermediate_format || argno == argc - 1)
79438fd1498Szrj 	{
795*58e805e6Szrj 	  process_all_functions ();
79638fd1498Szrj 	  generate_results (argv[argno]);
79738fd1498Szrj 	  release_structures ();
79838fd1498Szrj 	}
79938fd1498Szrj     }
80038fd1498Szrj 
80138fd1498Szrj   return 0;
80238fd1498Szrj }
80338fd1498Szrj 
80438fd1498Szrj /* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
80538fd1498Szrj    otherwise the output of --help.  */
80638fd1498Szrj 
80738fd1498Szrj static void
print_usage(int error_p)80838fd1498Szrj print_usage (int error_p)
80938fd1498Szrj {
81038fd1498Szrj   FILE *file = error_p ? stderr : stdout;
81138fd1498Szrj   int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
81238fd1498Szrj 
81338fd1498Szrj   fnotice (file, "Usage: gcov [OPTION...] SOURCE|OBJ...\n\n");
81438fd1498Szrj   fnotice (file, "Print code coverage information.\n\n");
81538fd1498Szrj   fnotice (file, "  -a, --all-blocks                Show information for every basic block\n");
81638fd1498Szrj   fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
81738fd1498Szrj   fnotice (file, "  -c, --branch-counts             Output counts of branches taken\n\
81838fd1498Szrj                                     rather than percentages\n");
81938fd1498Szrj   fnotice (file, "  -d, --display-progress          Display progress information\n");
82038fd1498Szrj   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
82138fd1498Szrj   fnotice (file, "  -h, --help                      Print this help, then exit\n");
82238fd1498Szrj   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
82338fd1498Szrj   fnotice (file, "  -j, --human-readable            Output human readable numbers\n");
82438fd1498Szrj   fnotice (file, "  -k, --use-colors                Emit colored output\n");
82538fd1498Szrj   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
82638fd1498Szrj                                     source files\n");
82738fd1498Szrj   fnotice (file, "  -m, --demangled-names           Output demangled function names\n");
82838fd1498Szrj   fnotice (file, "  -n, --no-output                 Do not create an output file\n");
82938fd1498Szrj   fnotice (file, "  -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
83038fd1498Szrj   fnotice (file, "  -p, --preserve-paths            Preserve all pathname components\n");
83138fd1498Szrj   fnotice (file, "  -r, --relative-only             Only show data for relative sources\n");
83238fd1498Szrj   fnotice (file, "  -s, --source-prefix DIR         Source prefix to elide\n");
83338fd1498Szrj   fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
83438fd1498Szrj   fnotice (file, "  -v, --version                   Print version number, then exit\n");
83538fd1498Szrj   fnotice (file, "  -w, --verbose                   Print verbose informations\n");
83638fd1498Szrj   fnotice (file, "  -x, --hash-filenames            Hash long pathnames\n");
83738fd1498Szrj   fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
83838fd1498Szrj 	   bug_report_url);
83938fd1498Szrj   exit (status);
84038fd1498Szrj }
84138fd1498Szrj 
84238fd1498Szrj /* Print version information and exit.  */
84338fd1498Szrj 
84438fd1498Szrj static void
print_version(void)84538fd1498Szrj print_version (void)
84638fd1498Szrj {
84738fd1498Szrj   fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
84838fd1498Szrj   fprintf (stdout, "Copyright %s 2018 Free Software Foundation, Inc.\n",
84938fd1498Szrj 	   _("(C)"));
85038fd1498Szrj   fnotice (stdout,
85138fd1498Szrj 	   _("This is free software; see the source for copying conditions.\n"
85238fd1498Szrj 	     "There is NO warranty; not even for MERCHANTABILITY or \n"
85338fd1498Szrj 	     "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
85438fd1498Szrj   exit (SUCCESS_EXIT_CODE);
85538fd1498Szrj }
85638fd1498Szrj 
85738fd1498Szrj static const struct option options[] =
85838fd1498Szrj {
85938fd1498Szrj   { "help",                 no_argument,       NULL, 'h' },
86038fd1498Szrj   { "version",              no_argument,       NULL, 'v' },
86138fd1498Szrj   { "verbose",              no_argument,       NULL, 'w' },
86238fd1498Szrj   { "all-blocks",           no_argument,       NULL, 'a' },
86338fd1498Szrj   { "branch-probabilities", no_argument,       NULL, 'b' },
86438fd1498Szrj   { "branch-counts",        no_argument,       NULL, 'c' },
86538fd1498Szrj   { "intermediate-format",  no_argument,       NULL, 'i' },
86638fd1498Szrj   { "human-readable",	    no_argument,       NULL, 'j' },
86738fd1498Szrj   { "no-output",            no_argument,       NULL, 'n' },
86838fd1498Szrj   { "long-file-names",      no_argument,       NULL, 'l' },
86938fd1498Szrj   { "function-summaries",   no_argument,       NULL, 'f' },
87038fd1498Szrj   { "demangled-names",      no_argument,       NULL, 'm' },
87138fd1498Szrj   { "preserve-paths",       no_argument,       NULL, 'p' },
87238fd1498Szrj   { "relative-only",        no_argument,       NULL, 'r' },
87338fd1498Szrj   { "object-directory",     required_argument, NULL, 'o' },
87438fd1498Szrj   { "object-file",          required_argument, NULL, 'o' },
87538fd1498Szrj   { "source-prefix",        required_argument, NULL, 's' },
87638fd1498Szrj   { "unconditional-branches", no_argument,     NULL, 'u' },
87738fd1498Szrj   { "display-progress",     no_argument,       NULL, 'd' },
87838fd1498Szrj   { "hash-filenames",	    no_argument,       NULL, 'x' },
87938fd1498Szrj   { "use-colors",	    no_argument,       NULL, 'k' },
88038fd1498Szrj   { 0, 0, 0, 0 }
88138fd1498Szrj };
88238fd1498Szrj 
88338fd1498Szrj /* Process args, return index to first non-arg.  */
88438fd1498Szrj 
88538fd1498Szrj static int
process_args(int argc,char ** argv)88638fd1498Szrj process_args (int argc, char **argv)
88738fd1498Szrj {
88838fd1498Szrj   int opt;
88938fd1498Szrj 
89038fd1498Szrj   const char *opts = "abcdfhijklmno:prs:uvwx";
89138fd1498Szrj   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
89238fd1498Szrj     {
89338fd1498Szrj       switch (opt)
89438fd1498Szrj 	{
89538fd1498Szrj 	case 'a':
89638fd1498Szrj 	  flag_all_blocks = 1;
89738fd1498Szrj 	  break;
89838fd1498Szrj 	case 'b':
89938fd1498Szrj 	  flag_branches = 1;
90038fd1498Szrj 	  break;
90138fd1498Szrj 	case 'c':
90238fd1498Szrj 	  flag_counts = 1;
90338fd1498Szrj 	  break;
90438fd1498Szrj 	case 'f':
90538fd1498Szrj 	  flag_function_summary = 1;
90638fd1498Szrj 	  break;
90738fd1498Szrj 	case 'h':
90838fd1498Szrj 	  print_usage (false);
90938fd1498Szrj 	  /* print_usage will exit.  */
91038fd1498Szrj 	case 'l':
91138fd1498Szrj 	  flag_long_names = 1;
91238fd1498Szrj 	  break;
91338fd1498Szrj 	case 'j':
91438fd1498Szrj 	  flag_human_readable_numbers = 1;
91538fd1498Szrj 	  break;
91638fd1498Szrj 	case 'k':
91738fd1498Szrj 	  flag_use_colors = 1;
91838fd1498Szrj 	  break;
91938fd1498Szrj 	case 'm':
92038fd1498Szrj 	  flag_demangled_names = 1;
92138fd1498Szrj 	  break;
92238fd1498Szrj 	case 'n':
92338fd1498Szrj 	  flag_gcov_file = 0;
92438fd1498Szrj 	  break;
92538fd1498Szrj 	case 'o':
92638fd1498Szrj 	  object_directory = optarg;
92738fd1498Szrj 	  break;
92838fd1498Szrj 	case 's':
92938fd1498Szrj 	  source_prefix = optarg;
93038fd1498Szrj 	  source_length = strlen (source_prefix);
93138fd1498Szrj 	  break;
93238fd1498Szrj 	case 'r':
93338fd1498Szrj 	  flag_relative_only = 1;
93438fd1498Szrj 	  break;
93538fd1498Szrj 	case 'p':
93638fd1498Szrj 	  flag_preserve_paths = 1;
93738fd1498Szrj 	  break;
93838fd1498Szrj 	case 'u':
93938fd1498Szrj 	  flag_unconditional = 1;
94038fd1498Szrj 	  break;
94138fd1498Szrj 	case 'i':
94238fd1498Szrj           flag_intermediate_format = 1;
94338fd1498Szrj           flag_gcov_file = 1;
94438fd1498Szrj           break;
94538fd1498Szrj         case 'd':
94638fd1498Szrj           flag_display_progress = 1;
94738fd1498Szrj           break;
94838fd1498Szrj 	case 'x':
94938fd1498Szrj 	  flag_hash_filenames = 1;
95038fd1498Szrj 	  break;
95138fd1498Szrj 	case 'w':
95238fd1498Szrj 	  flag_verbose = 1;
95338fd1498Szrj 	  break;
95438fd1498Szrj 	case 'v':
95538fd1498Szrj 	  print_version ();
95638fd1498Szrj 	  /* print_version will exit.  */
95738fd1498Szrj 	default:
95838fd1498Szrj 	  print_usage (true);
95938fd1498Szrj 	  /* print_usage will exit.  */
96038fd1498Szrj 	}
96138fd1498Szrj     }
96238fd1498Szrj 
96338fd1498Szrj   return optind;
96438fd1498Szrj }
96538fd1498Szrj 
96638fd1498Szrj /* Output intermediate LINE sitting on LINE_NUM to output file F.  */
96738fd1498Szrj 
96838fd1498Szrj static void
output_intermediate_line(FILE * f,line_info * line,unsigned line_num)96938fd1498Szrj output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
97038fd1498Szrj {
97138fd1498Szrj   if (!line->exists)
97238fd1498Szrj     return;
97338fd1498Szrj 
97438fd1498Szrj   fprintf (f, "lcount:%u,%s,%d\n", line_num,
97538fd1498Szrj 	   format_gcov (line->count, 0, -1),
97638fd1498Szrj 	   line->has_unexecuted_block);
97738fd1498Szrj 
97838fd1498Szrj   vector<arc_info *>::const_iterator it;
97938fd1498Szrj   if (flag_branches)
98038fd1498Szrj     for (it = line->branches.begin (); it != line->branches.end ();
98138fd1498Szrj 	 it++)
98238fd1498Szrj       {
98338fd1498Szrj 	if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
98438fd1498Szrj 	  {
98538fd1498Szrj 	    const char *branch_type;
98638fd1498Szrj 	    /* branch:<line_num>,<branch_coverage_infoype>
98738fd1498Szrj 	       branch_coverage_infoype
98838fd1498Szrj 	       : notexec (Branch not executed)
98938fd1498Szrj 	       : taken (Branch executed and taken)
99038fd1498Szrj 	       : nottaken (Branch executed, but not taken)
99138fd1498Szrj 	       */
99238fd1498Szrj 	    if ((*it)->src->count)
99338fd1498Szrj 		 branch_type
99438fd1498Szrj 			= ((*it)->count > 0) ? "taken" : "nottaken";
99538fd1498Szrj 	    else
99638fd1498Szrj 	      branch_type = "notexec";
99738fd1498Szrj 	    fprintf (f, "branch:%d,%s\n", line_num, branch_type);
99838fd1498Szrj 	  }
99938fd1498Szrj       }
100038fd1498Szrj }
100138fd1498Szrj 
100238fd1498Szrj /* Get the name of the gcov file.  The return value must be free'd.
100338fd1498Szrj 
100438fd1498Szrj    It appends the '.gcov' extension to the *basename* of the file.
100538fd1498Szrj    The resulting file name will be in PWD.
100638fd1498Szrj 
100738fd1498Szrj    e.g.,
100838fd1498Szrj    input: foo.da,       output: foo.da.gcov
100938fd1498Szrj    input: a/b/foo.cc,   output: foo.cc.gcov  */
101038fd1498Szrj 
101138fd1498Szrj static char *
get_gcov_intermediate_filename(const char * file_name)101238fd1498Szrj get_gcov_intermediate_filename (const char *file_name)
101338fd1498Szrj {
101438fd1498Szrj   const char *gcov = ".gcov";
101538fd1498Szrj   char *result;
101638fd1498Szrj   const char *cptr;
101738fd1498Szrj 
101838fd1498Szrj   /* Find the 'basename'.  */
101938fd1498Szrj   cptr = lbasename (file_name);
102038fd1498Szrj 
102138fd1498Szrj   result = XNEWVEC (char, strlen (cptr) + strlen (gcov) + 1);
102238fd1498Szrj   sprintf (result, "%s%s", cptr, gcov);
102338fd1498Szrj 
102438fd1498Szrj   return result;
102538fd1498Szrj }
102638fd1498Szrj 
102738fd1498Szrj /* Output the result in intermediate format used by 'lcov'.
102838fd1498Szrj 
102938fd1498Szrj The intermediate format contains a single file named 'foo.cc.gcov',
103038fd1498Szrj with no source code included.
103138fd1498Szrj 
103238fd1498Szrj The default gcov outputs multiple files: 'foo.cc.gcov',
103338fd1498Szrj 'iostream.gcov', 'ios_base.h.gcov', etc. with source code
103438fd1498Szrj included. Instead the intermediate format here outputs only a single
103538fd1498Szrj file 'foo.cc.gcov' similar to the above example. */
103638fd1498Szrj 
103738fd1498Szrj static void
output_intermediate_file(FILE * gcov_file,source_info * src)103838fd1498Szrj output_intermediate_file (FILE *gcov_file, source_info *src)
103938fd1498Szrj {
104038fd1498Szrj   fprintf (gcov_file, "version:%s\n", version_string);
104138fd1498Szrj   fprintf (gcov_file, "file:%s\n", src->name);    /* source file name */
104238fd1498Szrj 
104338fd1498Szrj   std::sort (src->functions.begin (), src->functions.end (),
104438fd1498Szrj 	     function_line_start_cmp ());
104538fd1498Szrj   for (vector<function_info *>::iterator it = src->functions.begin ();
104638fd1498Szrj        it != src->functions.end (); it++)
104738fd1498Szrj     {
104838fd1498Szrj       /* function:<name>,<line_number>,<execution_count> */
104938fd1498Szrj       fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line,
105038fd1498Szrj 	       (*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1),
105138fd1498Szrj 	       flag_demangled_names ? (*it)->demangled_name : (*it)->name);
105238fd1498Szrj     }
105338fd1498Szrj 
105438fd1498Szrj   for (unsigned line_num = 1; line_num <= src->lines.size (); line_num++)
105538fd1498Szrj     {
105638fd1498Szrj       vector<function_info *> fns = src->get_functions_at_location (line_num);
105738fd1498Szrj 
105838fd1498Szrj       /* Print first group functions that begin on the line.  */
105938fd1498Szrj       for (vector<function_info *>::iterator it2 = fns.begin ();
106038fd1498Szrj 	   it2 != fns.end (); it2++)
106138fd1498Szrj 	{
106238fd1498Szrj 	  vector<line_info> &lines = (*it2)->lines;
106338fd1498Szrj 	  for (unsigned i = 0; i < lines.size (); i++)
106438fd1498Szrj 	    {
106538fd1498Szrj 	      line_info *line = &lines[i];
106638fd1498Szrj 	      output_intermediate_line (gcov_file, line, line_num + i);
106738fd1498Szrj 	    }
106838fd1498Szrj 	}
106938fd1498Szrj 
107038fd1498Szrj       /* Follow with lines associated with the source file.  */
1071*58e805e6Szrj       if (line_num < src->lines.size ())
107238fd1498Szrj 	output_intermediate_line (gcov_file, &src->lines[line_num], line_num);
107338fd1498Szrj     }
107438fd1498Szrj }
107538fd1498Szrj 
107638fd1498Szrj /* Function start pair.  */
107738fd1498Szrj struct function_start
107838fd1498Szrj {
107938fd1498Szrj   unsigned source_file_idx;
108038fd1498Szrj   unsigned start_line;
108138fd1498Szrj };
108238fd1498Szrj 
108338fd1498Szrj /* Traits class for function start hash maps below.  */
108438fd1498Szrj 
108538fd1498Szrj struct function_start_pair_hash : typed_noop_remove <function_start>
108638fd1498Szrj {
108738fd1498Szrj   typedef function_start value_type;
108838fd1498Szrj   typedef function_start compare_type;
108938fd1498Szrj 
109038fd1498Szrj   static hashval_t
hashfunction_start_pair_hash109138fd1498Szrj   hash (const function_start &ref)
109238fd1498Szrj   {
109338fd1498Szrj     inchash::hash hstate (0);
109438fd1498Szrj     hstate.add_int (ref.source_file_idx);
109538fd1498Szrj     hstate.add_int (ref.start_line);
109638fd1498Szrj     return hstate.end ();
109738fd1498Szrj   }
109838fd1498Szrj 
109938fd1498Szrj   static bool
equalfunction_start_pair_hash110038fd1498Szrj   equal (const function_start &ref1, const function_start &ref2)
110138fd1498Szrj   {
110238fd1498Szrj     return (ref1.source_file_idx == ref2.source_file_idx
110338fd1498Szrj 	    && ref1.start_line == ref2.start_line);
110438fd1498Szrj   }
110538fd1498Szrj 
110638fd1498Szrj   static void
mark_deletedfunction_start_pair_hash110738fd1498Szrj   mark_deleted (function_start &ref)
110838fd1498Szrj   {
110938fd1498Szrj     ref.start_line = ~1U;
111038fd1498Szrj   }
111138fd1498Szrj 
111238fd1498Szrj   static void
mark_emptyfunction_start_pair_hash111338fd1498Szrj   mark_empty (function_start &ref)
111438fd1498Szrj   {
111538fd1498Szrj     ref.start_line = ~2U;
111638fd1498Szrj   }
111738fd1498Szrj 
111838fd1498Szrj   static bool
is_deletedfunction_start_pair_hash111938fd1498Szrj   is_deleted (const function_start &ref)
112038fd1498Szrj   {
112138fd1498Szrj     return ref.start_line == ~1U;
112238fd1498Szrj   }
112338fd1498Szrj 
112438fd1498Szrj   static bool
is_emptyfunction_start_pair_hash112538fd1498Szrj   is_empty (const function_start &ref)
112638fd1498Szrj   {
112738fd1498Szrj     return ref.start_line == ~2U;
112838fd1498Szrj   }
112938fd1498Szrj };
113038fd1498Szrj 
113138fd1498Szrj /* Process a single input file.  */
113238fd1498Szrj 
113338fd1498Szrj static void
process_file(const char * file_name)113438fd1498Szrj process_file (const char *file_name)
113538fd1498Szrj {
113638fd1498Szrj   create_file_names (file_name);
113738fd1498Szrj   read_graph_file ();
113838fd1498Szrj   read_count_file ();
1139*58e805e6Szrj }
114038fd1498Szrj 
1141*58e805e6Szrj /* Process all functions in all files.  */
1142*58e805e6Szrj 
1143*58e805e6Szrj static void
process_all_functions(void)1144*58e805e6Szrj process_all_functions (void)
1145*58e805e6Szrj {
114638fd1498Szrj   hash_map<function_start_pair_hash, function_info *> fn_map;
114738fd1498Szrj 
114838fd1498Szrj   /* Identify group functions.  */
114938fd1498Szrj   for (vector<function_info *>::iterator it = functions.begin ();
115038fd1498Szrj        it != functions.end (); it++)
115138fd1498Szrj     if (!(*it)->artificial)
115238fd1498Szrj       {
115338fd1498Szrj 	function_start needle;
115438fd1498Szrj 	needle.source_file_idx = (*it)->src;
115538fd1498Szrj 	needle.start_line = (*it)->start_line;
115638fd1498Szrj 
115738fd1498Szrj 	function_info **slot = fn_map.get (needle);
115838fd1498Szrj 	if (slot)
115938fd1498Szrj 	  {
116038fd1498Szrj 	    (*slot)->is_group = 1;
116138fd1498Szrj 	    (*it)->is_group = 1;
116238fd1498Szrj 	  }
116338fd1498Szrj 	else
116438fd1498Szrj 	  fn_map.put (needle, *it);
116538fd1498Szrj       }
116638fd1498Szrj 
116738fd1498Szrj   /* Remove all artificial function.  */
116838fd1498Szrj   functions.erase (remove_if (functions.begin (), functions.end (),
116938fd1498Szrj 			      function_info::is_artificial), functions.end ());
117038fd1498Szrj 
117138fd1498Szrj   for (vector<function_info *>::iterator it = functions.begin ();
117238fd1498Szrj        it != functions.end (); it++)
117338fd1498Szrj     {
117438fd1498Szrj       function_info *fn = *it;
117538fd1498Szrj       unsigned src = fn->src;
117638fd1498Szrj 
117738fd1498Szrj       if (!fn->counts.empty () || no_data_file)
117838fd1498Szrj 	{
117938fd1498Szrj 	  source_info *s = &sources[src];
118038fd1498Szrj 	  s->functions.push_back (fn);
118138fd1498Szrj 
118238fd1498Szrj 	  /* Mark last line in files touched by function.  */
118338fd1498Szrj 	  for (unsigned block_no = 0; block_no != fn->blocks.size ();
118438fd1498Szrj 	       block_no++)
118538fd1498Szrj 	    {
118638fd1498Szrj 	      block_info *block = &fn->blocks[block_no];
118738fd1498Szrj 	      for (unsigned i = 0; i < block->locations.size (); i++)
118838fd1498Szrj 		{
118938fd1498Szrj 		  /* Sort lines of locations.  */
119038fd1498Szrj 		  sort (block->locations[i].lines.begin (),
119138fd1498Szrj 			block->locations[i].lines.end ());
119238fd1498Szrj 
119338fd1498Szrj 		  if (!block->locations[i].lines.empty ())
119438fd1498Szrj 		    {
119538fd1498Szrj 		      s = &sources[block->locations[i].source_file_idx];
119638fd1498Szrj 		      unsigned last_line
119738fd1498Szrj 			= block->locations[i].lines.back ();
119838fd1498Szrj 
119938fd1498Szrj 		      /* Record new lines for the function.  */
120038fd1498Szrj 		      if (last_line >= s->lines.size ())
120138fd1498Szrj 			{
120238fd1498Szrj 			  s = &sources[block->locations[i].source_file_idx];
120338fd1498Szrj 			  unsigned last_line
120438fd1498Szrj 			    = block->locations[i].lines.back ();
120538fd1498Szrj 
120638fd1498Szrj 			  /* Record new lines for the function.  */
120738fd1498Szrj 			  if (last_line >= s->lines.size ())
120838fd1498Szrj 			    {
120938fd1498Szrj 			      /* Record new lines for a source file.  */
121038fd1498Szrj 			      s->lines.resize (last_line + 1);
121138fd1498Szrj 			    }
121238fd1498Szrj 			}
121338fd1498Szrj 		    }
121438fd1498Szrj 		}
121538fd1498Szrj 	    }
121638fd1498Szrj 
121738fd1498Szrj 	  /* Allocate lines for group function, following start_line
121838fd1498Szrj 	     and end_line information of the function.  */
121938fd1498Szrj 	  if (fn->is_group)
122038fd1498Szrj 	    fn->lines.resize (fn->end_line - fn->start_line + 1);
122138fd1498Szrj 
122238fd1498Szrj 	  solve_flow_graph (fn);
122338fd1498Szrj 	  if (fn->has_catch)
122438fd1498Szrj 	    find_exception_blocks (fn);
122538fd1498Szrj 	}
122638fd1498Szrj       else
122738fd1498Szrj 	{
122838fd1498Szrj 	  /* The function was not in the executable -- some other
122938fd1498Szrj 	     instance must have been selected.  */
123038fd1498Szrj 	}
123138fd1498Szrj     }
123238fd1498Szrj }
123338fd1498Szrj 
123438fd1498Szrj static void
output_gcov_file(const char * file_name,source_info * src)123538fd1498Szrj output_gcov_file (const char *file_name, source_info *src)
123638fd1498Szrj {
123738fd1498Szrj   char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name);
123838fd1498Szrj 
123938fd1498Szrj   if (src->coverage.lines)
124038fd1498Szrj     {
124138fd1498Szrj       FILE *gcov_file = fopen (gcov_file_name, "w");
124238fd1498Szrj       if (gcov_file)
124338fd1498Szrj 	{
124438fd1498Szrj 	  fnotice (stdout, "Creating '%s'\n", gcov_file_name);
124538fd1498Szrj 	  output_lines (gcov_file, src);
124638fd1498Szrj 	  if (ferror (gcov_file))
124738fd1498Szrj 	    fnotice (stderr, "Error writing output file '%s'\n",
124838fd1498Szrj 		     gcov_file_name);
124938fd1498Szrj 	  fclose (gcov_file);
125038fd1498Szrj 	}
125138fd1498Szrj       else
125238fd1498Szrj 	fnotice (stderr, "Could not open output file '%s'\n", gcov_file_name);
125338fd1498Szrj     }
125438fd1498Szrj   else
125538fd1498Szrj     {
125638fd1498Szrj       unlink (gcov_file_name);
125738fd1498Szrj       fnotice (stdout, "Removing '%s'\n", gcov_file_name);
125838fd1498Szrj     }
125938fd1498Szrj   free (gcov_file_name);
126038fd1498Szrj }
126138fd1498Szrj 
126238fd1498Szrj static void
generate_results(const char * file_name)126338fd1498Szrj generate_results (const char *file_name)
126438fd1498Szrj {
126538fd1498Szrj   FILE *gcov_intermediate_file = NULL;
126638fd1498Szrj   char *gcov_intermediate_filename = NULL;
126738fd1498Szrj 
126838fd1498Szrj   for (vector<function_info *>::iterator it = functions.begin ();
126938fd1498Szrj        it != functions.end (); it++)
127038fd1498Szrj     {
127138fd1498Szrj       function_info *fn = *it;
127238fd1498Szrj       coverage_info coverage;
127338fd1498Szrj 
127438fd1498Szrj       memset (&coverage, 0, sizeof (coverage));
127538fd1498Szrj       coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
127638fd1498Szrj       add_line_counts (flag_function_summary ? &coverage : NULL, fn);
127738fd1498Szrj       if (flag_function_summary)
127838fd1498Szrj 	{
127938fd1498Szrj 	  function_summary (&coverage, "Function");
128038fd1498Szrj 	  fnotice (stdout, "\n");
128138fd1498Szrj 	}
128238fd1498Szrj     }
128338fd1498Szrj 
128438fd1498Szrj   name_map needle;
128538fd1498Szrj 
128638fd1498Szrj   if (file_name)
128738fd1498Szrj     {
128838fd1498Szrj       needle.name = file_name;
128938fd1498Szrj       vector<name_map>::iterator it = std::find (names.begin (), names.end (),
129038fd1498Szrj 						 needle);
129138fd1498Szrj       if (it != names.end ())
129238fd1498Szrj 	file_name = sources[it->src].coverage.name;
129338fd1498Szrj       else
129438fd1498Szrj 	file_name = canonicalize_name (file_name);
129538fd1498Szrj     }
129638fd1498Szrj 
129738fd1498Szrj   if (flag_gcov_file && flag_intermediate_format)
129838fd1498Szrj     {
129938fd1498Szrj       /* Open the intermediate file.  */
130038fd1498Szrj       gcov_intermediate_filename = get_gcov_intermediate_filename (file_name);
130138fd1498Szrj       gcov_intermediate_file = fopen (gcov_intermediate_filename, "w");
130238fd1498Szrj       if (!gcov_intermediate_file)
130338fd1498Szrj 	{
130438fd1498Szrj 	  fnotice (stderr, "Cannot open intermediate output file %s\n",
130538fd1498Szrj 		   gcov_intermediate_filename);
130638fd1498Szrj 	  return;
130738fd1498Szrj 	}
130838fd1498Szrj     }
130938fd1498Szrj 
131038fd1498Szrj   for (vector<source_info>::iterator it = sources.begin ();
131138fd1498Szrj        it != sources.end (); it++)
131238fd1498Szrj     {
131338fd1498Szrj       source_info *src = &(*it);
131438fd1498Szrj       if (flag_relative_only)
131538fd1498Szrj 	{
131638fd1498Szrj 	  /* Ignore this source, if it is an absolute path (after
131738fd1498Szrj 	     source prefix removal).  */
131838fd1498Szrj 	  char first = src->coverage.name[0];
131938fd1498Szrj 
132038fd1498Szrj #if HAVE_DOS_BASED_FILE_SYSTEM
132138fd1498Szrj 	  if (first && src->coverage.name[1] == ':')
132238fd1498Szrj 	    first = src->coverage.name[2];
132338fd1498Szrj #endif
132438fd1498Szrj 	  if (IS_DIR_SEPARATOR (first))
132538fd1498Szrj 	    continue;
132638fd1498Szrj 	}
132738fd1498Szrj 
132838fd1498Szrj       accumulate_line_counts (src);
132938fd1498Szrj       function_summary (&src->coverage, "File");
133038fd1498Szrj       total_lines += src->coverage.lines;
133138fd1498Szrj       total_executed += src->coverage.lines_executed;
133238fd1498Szrj       if (flag_gcov_file)
133338fd1498Szrj 	{
133438fd1498Szrj 	  if (flag_intermediate_format)
133538fd1498Szrj 	    /* Output the intermediate format without requiring source
133638fd1498Szrj 	       files.  This outputs a section to a *single* file.  */
133738fd1498Szrj 	    output_intermediate_file (gcov_intermediate_file, src);
133838fd1498Szrj 	  else
133938fd1498Szrj 	    output_gcov_file (file_name, src);
134038fd1498Szrj 	  fnotice (stdout, "\n");
134138fd1498Szrj 	}
134238fd1498Szrj     }
134338fd1498Szrj 
134438fd1498Szrj   if (flag_gcov_file && flag_intermediate_format)
134538fd1498Szrj     {
134638fd1498Szrj       /* Now we've finished writing the intermediate file.  */
134738fd1498Szrj       fclose (gcov_intermediate_file);
134838fd1498Szrj       XDELETEVEC (gcov_intermediate_filename);
134938fd1498Szrj     }
135038fd1498Szrj 
135138fd1498Szrj   if (!file_name)
135238fd1498Szrj     executed_summary (total_lines, total_executed);
135338fd1498Szrj }
135438fd1498Szrj 
135538fd1498Szrj /* Release all memory used.  */
135638fd1498Szrj 
135738fd1498Szrj static void
release_structures(void)135838fd1498Szrj release_structures (void)
135938fd1498Szrj {
136038fd1498Szrj   for (vector<function_info *>::iterator it = functions.begin ();
136138fd1498Szrj        it != functions.end (); it++)
136238fd1498Szrj     delete (*it);
136338fd1498Szrj 
136438fd1498Szrj   sources.resize (0);
136538fd1498Szrj   names.resize (0);
136638fd1498Szrj   functions.resize (0);
136738fd1498Szrj }
136838fd1498Szrj 
136938fd1498Szrj /* Generate the names of the graph and data files.  If OBJECT_DIRECTORY
137038fd1498Szrj    is not specified, these are named from FILE_NAME sans extension.  If
137138fd1498Szrj    OBJECT_DIRECTORY is specified and is a directory, the files are in that
137238fd1498Szrj    directory, but named from the basename of the FILE_NAME, sans extension.
137338fd1498Szrj    Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file*
137438fd1498Szrj    and the data files are named from that.  */
137538fd1498Szrj 
137638fd1498Szrj static void
create_file_names(const char * file_name)137738fd1498Szrj create_file_names (const char *file_name)
137838fd1498Szrj {
137938fd1498Szrj   char *cptr;
138038fd1498Szrj   char *name;
138138fd1498Szrj   int length = strlen (file_name);
138238fd1498Szrj   int base;
138338fd1498Szrj 
138438fd1498Szrj   /* Free previous file names.  */
138538fd1498Szrj   free (bbg_file_name);
138638fd1498Szrj   free (da_file_name);
138738fd1498Szrj   da_file_name = bbg_file_name = NULL;
138838fd1498Szrj   bbg_file_time = 0;
138938fd1498Szrj   bbg_stamp = 0;
139038fd1498Szrj 
139138fd1498Szrj   if (object_directory && object_directory[0])
139238fd1498Szrj     {
139338fd1498Szrj       struct stat status;
139438fd1498Szrj 
139538fd1498Szrj       length += strlen (object_directory) + 2;
139638fd1498Szrj       name = XNEWVEC (char, length);
139738fd1498Szrj       name[0] = 0;
139838fd1498Szrj 
139938fd1498Szrj       base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
140038fd1498Szrj       strcat (name, object_directory);
140138fd1498Szrj       if (base && (!IS_DIR_SEPARATOR (name[strlen (name) - 1])))
140238fd1498Szrj 	strcat (name, "/");
140338fd1498Szrj     }
140438fd1498Szrj   else
140538fd1498Szrj     {
140638fd1498Szrj       name = XNEWVEC (char, length + 1);
140738fd1498Szrj       strcpy (name, file_name);
140838fd1498Szrj       base = 0;
140938fd1498Szrj     }
141038fd1498Szrj 
141138fd1498Szrj   if (base)
141238fd1498Szrj     {
141338fd1498Szrj       /* Append source file name.  */
141438fd1498Szrj       const char *cptr = lbasename (file_name);
141538fd1498Szrj       strcat (name, cptr ? cptr : file_name);
141638fd1498Szrj     }
141738fd1498Szrj 
141838fd1498Szrj   /* Remove the extension.  */
141938fd1498Szrj   cptr = strrchr (CONST_CAST (char *, lbasename (name)), '.');
142038fd1498Szrj   if (cptr)
142138fd1498Szrj     *cptr = 0;
142238fd1498Szrj 
142338fd1498Szrj   length = strlen (name);
142438fd1498Szrj 
142538fd1498Szrj   bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
142638fd1498Szrj   strcpy (bbg_file_name, name);
142738fd1498Szrj   strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
142838fd1498Szrj 
142938fd1498Szrj   da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
143038fd1498Szrj   strcpy (da_file_name, name);
143138fd1498Szrj   strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
143238fd1498Szrj 
143338fd1498Szrj   free (name);
143438fd1498Szrj   return;
143538fd1498Szrj }
143638fd1498Szrj 
143738fd1498Szrj /* Find or create a source file structure for FILE_NAME. Copies
143838fd1498Szrj    FILE_NAME on creation */
143938fd1498Szrj 
144038fd1498Szrj static unsigned
find_source(const char * file_name)144138fd1498Szrj find_source (const char *file_name)
144238fd1498Szrj {
144338fd1498Szrj   char *canon;
144438fd1498Szrj   unsigned idx;
144538fd1498Szrj   struct stat status;
144638fd1498Szrj 
144738fd1498Szrj   if (!file_name)
144838fd1498Szrj     file_name = "<unknown>";
144938fd1498Szrj 
145038fd1498Szrj   name_map needle;
145138fd1498Szrj   needle.name = file_name;
145238fd1498Szrj 
145338fd1498Szrj   vector<name_map>::iterator it = std::find (names.begin (), names.end (),
145438fd1498Szrj 					     needle);
145538fd1498Szrj   if (it != names.end ())
145638fd1498Szrj     {
145738fd1498Szrj       idx = it->src;
145838fd1498Szrj       goto check_date;
145938fd1498Szrj     }
146038fd1498Szrj 
146138fd1498Szrj   /* Not found, try the canonical name. */
146238fd1498Szrj   canon = canonicalize_name (file_name);
146338fd1498Szrj   needle.name = canon;
146438fd1498Szrj   it = std::find (names.begin (), names.end (), needle);
146538fd1498Szrj   if (it == names.end ())
146638fd1498Szrj     {
146738fd1498Szrj       /* Not found with canonical name, create a new source.  */
146838fd1498Szrj       source_info *src;
146938fd1498Szrj 
147038fd1498Szrj       idx = sources.size ();
147138fd1498Szrj       needle = name_map (canon, idx);
147238fd1498Szrj       names.push_back (needle);
147338fd1498Szrj 
147438fd1498Szrj       sources.push_back (source_info ());
147538fd1498Szrj       src = &sources.back ();
147638fd1498Szrj       src->name = canon;
147738fd1498Szrj       src->coverage.name = src->name;
147838fd1498Szrj       src->index = idx;
147938fd1498Szrj       if (source_length
148038fd1498Szrj #if HAVE_DOS_BASED_FILE_SYSTEM
148138fd1498Szrj 	  /* You lose if separators don't match exactly in the
148238fd1498Szrj 	     prefix.  */
148338fd1498Szrj 	  && !strncasecmp (source_prefix, src->coverage.name, source_length)
148438fd1498Szrj #else
148538fd1498Szrj 	  && !strncmp (source_prefix, src->coverage.name, source_length)
148638fd1498Szrj #endif
148738fd1498Szrj 	  && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
148838fd1498Szrj 	src->coverage.name += source_length + 1;
148938fd1498Szrj       if (!stat (src->name, &status))
149038fd1498Szrj 	src->file_time = status.st_mtime;
149138fd1498Szrj     }
149238fd1498Szrj   else
149338fd1498Szrj     idx = it->src;
149438fd1498Szrj 
149538fd1498Szrj   needle.name = file_name;
149638fd1498Szrj   if (std::find (names.begin (), names.end (), needle) == names.end ())
149738fd1498Szrj     {
149838fd1498Szrj       /* Append the non-canonical name.  */
149938fd1498Szrj       names.push_back (name_map (xstrdup (file_name), idx));
150038fd1498Szrj     }
150138fd1498Szrj 
150238fd1498Szrj   /* Resort the name map.  */
150338fd1498Szrj   std::sort (names.begin (), names.end ());
150438fd1498Szrj 
150538fd1498Szrj  check_date:
150638fd1498Szrj   if (sources[idx].file_time > bbg_file_time)
150738fd1498Szrj     {
150838fd1498Szrj       static int info_emitted;
150938fd1498Szrj 
151038fd1498Szrj       fnotice (stderr, "%s:source file is newer than notes file '%s'\n",
151138fd1498Szrj 	       file_name, bbg_file_name);
151238fd1498Szrj       if (!info_emitted)
151338fd1498Szrj 	{
151438fd1498Szrj 	  fnotice (stderr,
151538fd1498Szrj 		   "(the message is displayed only once per source file)\n");
151638fd1498Szrj 	  info_emitted = 1;
151738fd1498Szrj 	}
151838fd1498Szrj       sources[idx].file_time = 0;
151938fd1498Szrj     }
152038fd1498Szrj 
152138fd1498Szrj   return idx;
152238fd1498Szrj }
152338fd1498Szrj 
152438fd1498Szrj /* Read the notes file.  Save functions to FUNCTIONS global vector.  */
152538fd1498Szrj 
152638fd1498Szrj static void
read_graph_file(void)152738fd1498Szrj read_graph_file (void)
152838fd1498Szrj {
152938fd1498Szrj   unsigned version;
153038fd1498Szrj   unsigned current_tag = 0;
153138fd1498Szrj   unsigned tag;
153238fd1498Szrj 
153338fd1498Szrj   if (!gcov_open (bbg_file_name, 1))
153438fd1498Szrj     {
153538fd1498Szrj       fnotice (stderr, "%s:cannot open notes file\n", bbg_file_name);
153638fd1498Szrj       return;
153738fd1498Szrj     }
153838fd1498Szrj   bbg_file_time = gcov_time ();
153938fd1498Szrj   if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
154038fd1498Szrj     {
154138fd1498Szrj       fnotice (stderr, "%s:not a gcov notes file\n", bbg_file_name);
154238fd1498Szrj       gcov_close ();
154338fd1498Szrj       return;
154438fd1498Szrj     }
154538fd1498Szrj 
154638fd1498Szrj   version = gcov_read_unsigned ();
154738fd1498Szrj   if (version != GCOV_VERSION)
154838fd1498Szrj     {
154938fd1498Szrj       char v[4], e[4];
155038fd1498Szrj 
155138fd1498Szrj       GCOV_UNSIGNED2STRING (v, version);
155238fd1498Szrj       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
155338fd1498Szrj 
155438fd1498Szrj       fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
155538fd1498Szrj 	       bbg_file_name, v, e);
155638fd1498Szrj     }
155738fd1498Szrj   bbg_stamp = gcov_read_unsigned ();
155838fd1498Szrj   bbg_supports_has_unexecuted_blocks = gcov_read_unsigned ();
155938fd1498Szrj 
156038fd1498Szrj   function_info *fn = NULL;
156138fd1498Szrj   while ((tag = gcov_read_unsigned ()))
156238fd1498Szrj     {
156338fd1498Szrj       unsigned length = gcov_read_unsigned ();
156438fd1498Szrj       gcov_position_t base = gcov_position ();
156538fd1498Szrj 
156638fd1498Szrj       if (tag == GCOV_TAG_FUNCTION)
156738fd1498Szrj 	{
156838fd1498Szrj 	  char *function_name;
156938fd1498Szrj 	  unsigned ident;
157038fd1498Szrj 	  unsigned lineno_checksum, cfg_checksum;
157138fd1498Szrj 
157238fd1498Szrj 	  ident = gcov_read_unsigned ();
157338fd1498Szrj 	  lineno_checksum = gcov_read_unsigned ();
157438fd1498Szrj 	  cfg_checksum = gcov_read_unsigned ();
157538fd1498Szrj 	  function_name = xstrdup (gcov_read_string ());
157638fd1498Szrj 	  unsigned artificial = gcov_read_unsigned ();
157738fd1498Szrj 	  unsigned src_idx = find_source (gcov_read_string ());
157838fd1498Szrj 	  unsigned start_line = gcov_read_unsigned ();
157938fd1498Szrj 	  unsigned start_column = gcov_read_unsigned ();
158038fd1498Szrj 	  unsigned end_line = gcov_read_unsigned ();
158138fd1498Szrj 
158238fd1498Szrj 	  fn = new function_info ();
158338fd1498Szrj 	  functions.push_back (fn);
158438fd1498Szrj 	  fn->name = function_name;
158538fd1498Szrj 	  if (flag_demangled_names)
158638fd1498Szrj 	    {
158738fd1498Szrj 	      fn->demangled_name = cplus_demangle (fn->name, DMGL_PARAMS);
158838fd1498Szrj 	      if (!fn->demangled_name)
158938fd1498Szrj 		fn->demangled_name = fn->name;
159038fd1498Szrj 	    }
159138fd1498Szrj 	  fn->ident = ident;
159238fd1498Szrj 	  fn->lineno_checksum = lineno_checksum;
159338fd1498Szrj 	  fn->cfg_checksum = cfg_checksum;
159438fd1498Szrj 	  fn->src = src_idx;
159538fd1498Szrj 	  fn->start_line = start_line;
159638fd1498Szrj 	  fn->start_column = start_column;
159738fd1498Szrj 	  fn->end_line = end_line;
159838fd1498Szrj 	  fn->artificial = artificial;
159938fd1498Szrj 
160038fd1498Szrj 	  current_tag = tag;
160138fd1498Szrj 	}
160238fd1498Szrj       else if (fn && tag == GCOV_TAG_BLOCKS)
160338fd1498Szrj 	{
160438fd1498Szrj 	  if (!fn->blocks.empty ())
160538fd1498Szrj 	    fnotice (stderr, "%s:already seen blocks for '%s'\n",
160638fd1498Szrj 		     bbg_file_name, fn->name);
160738fd1498Szrj 	  else
160838fd1498Szrj 	    fn->blocks.resize (gcov_read_unsigned ());
160938fd1498Szrj 	}
161038fd1498Szrj       else if (fn && tag == GCOV_TAG_ARCS)
161138fd1498Szrj 	{
161238fd1498Szrj 	  unsigned src = gcov_read_unsigned ();
161338fd1498Szrj 	  fn->blocks[src].id = src;
161438fd1498Szrj 	  unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
161538fd1498Szrj 	  block_info *src_blk = &fn->blocks[src];
161638fd1498Szrj 	  unsigned mark_catches = 0;
161738fd1498Szrj 	  struct arc_info *arc;
161838fd1498Szrj 
161938fd1498Szrj 	  if (src >= fn->blocks.size () || fn->blocks[src].succ)
162038fd1498Szrj 	    goto corrupt;
162138fd1498Szrj 
162238fd1498Szrj 	  while (num_dests--)
162338fd1498Szrj 	    {
162438fd1498Szrj 	      unsigned dest = gcov_read_unsigned ();
162538fd1498Szrj 	      unsigned flags = gcov_read_unsigned ();
162638fd1498Szrj 
162738fd1498Szrj 	      if (dest >= fn->blocks.size ())
162838fd1498Szrj 		goto corrupt;
162938fd1498Szrj 	      arc = XCNEW (arc_info);
163038fd1498Szrj 
163138fd1498Szrj 	      arc->dst = &fn->blocks[dest];
163238fd1498Szrj 	      arc->src = src_blk;
163338fd1498Szrj 
163438fd1498Szrj 	      arc->count = 0;
163538fd1498Szrj 	      arc->count_valid = 0;
163638fd1498Szrj 	      arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
163738fd1498Szrj 	      arc->fake = !!(flags & GCOV_ARC_FAKE);
163838fd1498Szrj 	      arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
163938fd1498Szrj 
164038fd1498Szrj 	      arc->succ_next = src_blk->succ;
164138fd1498Szrj 	      src_blk->succ = arc;
164238fd1498Szrj 	      src_blk->num_succ++;
164338fd1498Szrj 
164438fd1498Szrj 	      arc->pred_next = fn->blocks[dest].pred;
164538fd1498Szrj 	      fn->blocks[dest].pred = arc;
164638fd1498Szrj 	      fn->blocks[dest].num_pred++;
164738fd1498Szrj 
164838fd1498Szrj 	      if (arc->fake)
164938fd1498Szrj 		{
165038fd1498Szrj 		  if (src)
165138fd1498Szrj 		    {
165238fd1498Szrj 		      /* Exceptional exit from this function, the
165338fd1498Szrj 			 source block must be a call.  */
165438fd1498Szrj 		      fn->blocks[src].is_call_site = 1;
165538fd1498Szrj 		      arc->is_call_non_return = 1;
165638fd1498Szrj 		      mark_catches = 1;
165738fd1498Szrj 		    }
165838fd1498Szrj 		  else
165938fd1498Szrj 		    {
166038fd1498Szrj 		      /* Non-local return from a callee of this
166138fd1498Szrj 			 function.  The destination block is a setjmp.  */
166238fd1498Szrj 		      arc->is_nonlocal_return = 1;
166338fd1498Szrj 		      fn->blocks[dest].is_nonlocal_return = 1;
166438fd1498Szrj 		    }
166538fd1498Szrj 		}
166638fd1498Szrj 
166738fd1498Szrj 	      if (!arc->on_tree)
166838fd1498Szrj 		fn->counts.push_back (0);
166938fd1498Szrj 	    }
167038fd1498Szrj 
167138fd1498Szrj 	  if (mark_catches)
167238fd1498Szrj 	    {
167338fd1498Szrj 	      /* We have a fake exit from this block.  The other
167438fd1498Szrj 		 non-fall through exits must be to catch handlers.
167538fd1498Szrj 		 Mark them as catch arcs.  */
167638fd1498Szrj 
167738fd1498Szrj 	      for (arc = src_blk->succ; arc; arc = arc->succ_next)
167838fd1498Szrj 		if (!arc->fake && !arc->fall_through)
167938fd1498Szrj 		  {
168038fd1498Szrj 		    arc->is_throw = 1;
168138fd1498Szrj 		    fn->has_catch = 1;
168238fd1498Szrj 		  }
168338fd1498Szrj 	    }
168438fd1498Szrj 	}
168538fd1498Szrj       else if (fn && tag == GCOV_TAG_LINES)
168638fd1498Szrj 	{
168738fd1498Szrj 	  unsigned blockno = gcov_read_unsigned ();
168838fd1498Szrj 	  block_info *block = &fn->blocks[blockno];
168938fd1498Szrj 
169038fd1498Szrj 	  if (blockno >= fn->blocks.size ())
169138fd1498Szrj 	    goto corrupt;
169238fd1498Szrj 
169338fd1498Szrj 	  while (true)
169438fd1498Szrj 	    {
169538fd1498Szrj 	      unsigned lineno = gcov_read_unsigned ();
169638fd1498Szrj 
169738fd1498Szrj 	      if (lineno)
169838fd1498Szrj 		block->locations.back ().lines.push_back (lineno);
169938fd1498Szrj 	      else
170038fd1498Szrj 		{
170138fd1498Szrj 		  const char *file_name = gcov_read_string ();
170238fd1498Szrj 
170338fd1498Szrj 		  if (!file_name)
170438fd1498Szrj 		    break;
170538fd1498Szrj 		  block->locations.push_back (block_location_info
170638fd1498Szrj 					      (find_source (file_name)));
170738fd1498Szrj 		}
170838fd1498Szrj 	    }
170938fd1498Szrj 	}
171038fd1498Szrj       else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
171138fd1498Szrj 	{
171238fd1498Szrj 	  fn = NULL;
171338fd1498Szrj 	  current_tag = 0;
171438fd1498Szrj 	}
171538fd1498Szrj       gcov_sync (base, length);
171638fd1498Szrj       if (gcov_is_error ())
171738fd1498Szrj 	{
171838fd1498Szrj 	corrupt:;
171938fd1498Szrj 	  fnotice (stderr, "%s:corrupted\n", bbg_file_name);
172038fd1498Szrj 	  break;
172138fd1498Szrj 	}
172238fd1498Szrj     }
172338fd1498Szrj   gcov_close ();
172438fd1498Szrj 
172538fd1498Szrj   if (functions.empty ())
172638fd1498Szrj     fnotice (stderr, "%s:no functions found\n", bbg_file_name);
172738fd1498Szrj }
172838fd1498Szrj 
172938fd1498Szrj /* Reads profiles from the count file and attach to each
173038fd1498Szrj    function. Return nonzero if fatal error.  */
173138fd1498Szrj 
173238fd1498Szrj static int
read_count_file(void)173338fd1498Szrj read_count_file (void)
173438fd1498Szrj {
173538fd1498Szrj   unsigned ix;
173638fd1498Szrj   unsigned version;
173738fd1498Szrj   unsigned tag;
173838fd1498Szrj   function_info *fn = NULL;
173938fd1498Szrj   int error = 0;
174038fd1498Szrj 
174138fd1498Szrj   if (!gcov_open (da_file_name, 1))
174238fd1498Szrj     {
174338fd1498Szrj       fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
174438fd1498Szrj 	       da_file_name);
174538fd1498Szrj       no_data_file = 1;
174638fd1498Szrj       return 0;
174738fd1498Szrj     }
174838fd1498Szrj   if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
174938fd1498Szrj     {
175038fd1498Szrj       fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
175138fd1498Szrj     cleanup:;
175238fd1498Szrj       gcov_close ();
175338fd1498Szrj       return 1;
175438fd1498Szrj     }
175538fd1498Szrj   version = gcov_read_unsigned ();
175638fd1498Szrj   if (version != GCOV_VERSION)
175738fd1498Szrj     {
175838fd1498Szrj       char v[4], e[4];
175938fd1498Szrj 
176038fd1498Szrj       GCOV_UNSIGNED2STRING (v, version);
176138fd1498Szrj       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
176238fd1498Szrj 
176338fd1498Szrj       fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
176438fd1498Szrj 	       da_file_name, v, e);
176538fd1498Szrj     }
176638fd1498Szrj   tag = gcov_read_unsigned ();
176738fd1498Szrj   if (tag != bbg_stamp)
176838fd1498Szrj     {
176938fd1498Szrj       fnotice (stderr, "%s:stamp mismatch with notes file\n", da_file_name);
177038fd1498Szrj       goto cleanup;
177138fd1498Szrj     }
177238fd1498Szrj 
177338fd1498Szrj   while ((tag = gcov_read_unsigned ()))
177438fd1498Szrj     {
177538fd1498Szrj       unsigned length = gcov_read_unsigned ();
177638fd1498Szrj       unsigned long base = gcov_position ();
177738fd1498Szrj 
177838fd1498Szrj       if (tag == GCOV_TAG_PROGRAM_SUMMARY)
177938fd1498Szrj 	{
178038fd1498Szrj 	  struct gcov_summary summary;
178138fd1498Szrj 	  gcov_read_summary (&summary);
178238fd1498Szrj 	  object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
178338fd1498Szrj 	  program_count++;
178438fd1498Szrj 	}
178538fd1498Szrj       else if (tag == GCOV_TAG_FUNCTION && !length)
178638fd1498Szrj 	; /* placeholder  */
178738fd1498Szrj       else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
178838fd1498Szrj 	{
178938fd1498Szrj 	  unsigned ident;
179038fd1498Szrj 
179138fd1498Szrj 	  /* Try to find the function in the list.  To speed up the
179238fd1498Szrj 	     search, first start from the last function found.  */
179338fd1498Szrj 	  ident = gcov_read_unsigned ();
179438fd1498Szrj 
179538fd1498Szrj 	  fn = NULL;
179638fd1498Szrj 	  for (vector<function_info *>::reverse_iterator it
179738fd1498Szrj 	       = functions.rbegin (); it != functions.rend (); it++)
179838fd1498Szrj 	    {
179938fd1498Szrj 	      if ((*it)->ident == ident)
180038fd1498Szrj 		{
180138fd1498Szrj 		  fn = *it;
180238fd1498Szrj 		  break;
180338fd1498Szrj 		}
180438fd1498Szrj 	    }
180538fd1498Szrj 
180638fd1498Szrj 	  if (!fn)
180738fd1498Szrj 	    ;
180838fd1498Szrj 	  else if (gcov_read_unsigned () != fn->lineno_checksum
180938fd1498Szrj 		   || gcov_read_unsigned () != fn->cfg_checksum)
181038fd1498Szrj 	    {
181138fd1498Szrj 	    mismatch:;
181238fd1498Szrj 	      fnotice (stderr, "%s:profile mismatch for '%s'\n",
181338fd1498Szrj 		       da_file_name, fn->name);
181438fd1498Szrj 	      goto cleanup;
181538fd1498Szrj 	    }
181638fd1498Szrj 	}
181738fd1498Szrj       else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
181838fd1498Szrj 	{
181938fd1498Szrj 	  if (length != GCOV_TAG_COUNTER_LENGTH (fn->counts.size ()))
182038fd1498Szrj 	    goto mismatch;
182138fd1498Szrj 
182238fd1498Szrj 	  for (ix = 0; ix != fn->counts.size (); ix++)
182338fd1498Szrj 	    fn->counts[ix] += gcov_read_counter ();
182438fd1498Szrj 	}
182538fd1498Szrj       gcov_sync (base, length);
182638fd1498Szrj       if ((error = gcov_is_error ()))
182738fd1498Szrj 	{
182838fd1498Szrj 	  fnotice (stderr,
182938fd1498Szrj 		   error < 0
183038fd1498Szrj 		   ? N_("%s:overflowed\n")
183138fd1498Szrj 		   : N_("%s:corrupted\n"),
183238fd1498Szrj 		   da_file_name);
183338fd1498Szrj 	  goto cleanup;
183438fd1498Szrj 	}
183538fd1498Szrj     }
183638fd1498Szrj 
183738fd1498Szrj   gcov_close ();
183838fd1498Szrj   return 0;
183938fd1498Szrj }
184038fd1498Szrj 
184138fd1498Szrj /* Solve the flow graph. Propagate counts from the instrumented arcs
184238fd1498Szrj    to the blocks and the uninstrumented arcs.  */
184338fd1498Szrj 
184438fd1498Szrj static void
solve_flow_graph(function_info * fn)184538fd1498Szrj solve_flow_graph (function_info *fn)
184638fd1498Szrj {
184738fd1498Szrj   unsigned ix;
184838fd1498Szrj   arc_info *arc;
184938fd1498Szrj   gcov_type *count_ptr = &fn->counts.front ();
185038fd1498Szrj   block_info *blk;
185138fd1498Szrj   block_info *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
185238fd1498Szrj   block_info *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
185338fd1498Szrj 
185438fd1498Szrj   /* The arcs were built in reverse order.  Fix that now.  */
185538fd1498Szrj   for (ix = fn->blocks.size (); ix--;)
185638fd1498Szrj     {
185738fd1498Szrj       arc_info *arc_p, *arc_n;
185838fd1498Szrj 
185938fd1498Szrj       for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
186038fd1498Szrj 	   arc_p = arc, arc = arc_n)
186138fd1498Szrj 	{
186238fd1498Szrj 	  arc_n = arc->succ_next;
186338fd1498Szrj 	  arc->succ_next = arc_p;
186438fd1498Szrj 	}
186538fd1498Szrj       fn->blocks[ix].succ = arc_p;
186638fd1498Szrj 
186738fd1498Szrj       for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
186838fd1498Szrj 	   arc_p = arc, arc = arc_n)
186938fd1498Szrj 	{
187038fd1498Szrj 	  arc_n = arc->pred_next;
187138fd1498Szrj 	  arc->pred_next = arc_p;
187238fd1498Szrj 	}
187338fd1498Szrj       fn->blocks[ix].pred = arc_p;
187438fd1498Szrj     }
187538fd1498Szrj 
187638fd1498Szrj   if (fn->blocks.size () < 2)
187738fd1498Szrj     fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
187838fd1498Szrj 	     bbg_file_name, fn->name);
187938fd1498Szrj   else
188038fd1498Szrj     {
188138fd1498Szrj       if (fn->blocks[ENTRY_BLOCK].num_pred)
188238fd1498Szrj 	fnotice (stderr, "%s:'%s' has arcs to entry block\n",
188338fd1498Szrj 		 bbg_file_name, fn->name);
188438fd1498Szrj       else
188538fd1498Szrj 	/* We can't deduce the entry block counts from the lack of
188638fd1498Szrj 	   predecessors.  */
188738fd1498Szrj 	fn->blocks[ENTRY_BLOCK].num_pred = ~(unsigned)0;
188838fd1498Szrj 
188938fd1498Szrj       if (fn->blocks[EXIT_BLOCK].num_succ)
189038fd1498Szrj 	fnotice (stderr, "%s:'%s' has arcs from exit block\n",
189138fd1498Szrj 		 bbg_file_name, fn->name);
189238fd1498Szrj       else
189338fd1498Szrj 	/* Likewise, we can't deduce exit block counts from the lack
189438fd1498Szrj 	   of its successors.  */
189538fd1498Szrj 	fn->blocks[EXIT_BLOCK].num_succ = ~(unsigned)0;
189638fd1498Szrj     }
189738fd1498Szrj 
189838fd1498Szrj   /* Propagate the measured counts, this must be done in the same
189938fd1498Szrj      order as the code in profile.c  */
190038fd1498Szrj   for (unsigned i = 0; i < fn->blocks.size (); i++)
190138fd1498Szrj     {
190238fd1498Szrj       blk = &fn->blocks[i];
190338fd1498Szrj       block_info const *prev_dst = NULL;
190438fd1498Szrj       int out_of_order = 0;
190538fd1498Szrj       int non_fake_succ = 0;
190638fd1498Szrj 
190738fd1498Szrj       for (arc = blk->succ; arc; arc = arc->succ_next)
190838fd1498Szrj 	{
190938fd1498Szrj 	  if (!arc->fake)
191038fd1498Szrj 	    non_fake_succ++;
191138fd1498Szrj 
191238fd1498Szrj 	  if (!arc->on_tree)
191338fd1498Szrj 	    {
191438fd1498Szrj 	      if (count_ptr)
191538fd1498Szrj 		arc->count = *count_ptr++;
191638fd1498Szrj 	      arc->count_valid = 1;
191738fd1498Szrj 	      blk->num_succ--;
191838fd1498Szrj 	      arc->dst->num_pred--;
191938fd1498Szrj 	    }
192038fd1498Szrj 	  if (prev_dst && prev_dst > arc->dst)
192138fd1498Szrj 	    out_of_order = 1;
192238fd1498Szrj 	  prev_dst = arc->dst;
192338fd1498Szrj 	}
192438fd1498Szrj       if (non_fake_succ == 1)
192538fd1498Szrj 	{
192638fd1498Szrj 	  /* If there is only one non-fake exit, it is an
192738fd1498Szrj 	     unconditional branch.  */
192838fd1498Szrj 	  for (arc = blk->succ; arc; arc = arc->succ_next)
192938fd1498Szrj 	    if (!arc->fake)
193038fd1498Szrj 	      {
193138fd1498Szrj 		arc->is_unconditional = 1;
193238fd1498Szrj 		/* If this block is instrumenting a call, it might be
193338fd1498Szrj 		   an artificial block. It is not artificial if it has
193438fd1498Szrj 		   a non-fallthrough exit, or the destination of this
193538fd1498Szrj 		   arc has more than one entry.  Mark the destination
193638fd1498Szrj 		   block as a return site, if none of those conditions
193738fd1498Szrj 		   hold.  */
193838fd1498Szrj 		if (blk->is_call_site && arc->fall_through
193938fd1498Szrj 		    && arc->dst->pred == arc && !arc->pred_next)
194038fd1498Szrj 		  arc->dst->is_call_return = 1;
194138fd1498Szrj 	      }
194238fd1498Szrj 	}
194338fd1498Szrj 
194438fd1498Szrj       /* Sort the successor arcs into ascending dst order. profile.c
194538fd1498Szrj 	 normally produces arcs in the right order, but sometimes with
194638fd1498Szrj 	 one or two out of order.  We're not using a particularly
194738fd1498Szrj 	 smart sort.  */
194838fd1498Szrj       if (out_of_order)
194938fd1498Szrj 	{
195038fd1498Szrj 	  arc_info *start = blk->succ;
195138fd1498Szrj 	  unsigned changes = 1;
195238fd1498Szrj 
195338fd1498Szrj 	  while (changes)
195438fd1498Szrj 	    {
195538fd1498Szrj 	      arc_info *arc, *arc_p, *arc_n;
195638fd1498Szrj 
195738fd1498Szrj 	      changes = 0;
195838fd1498Szrj 	      for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
195938fd1498Szrj 		{
196038fd1498Szrj 		  if (arc->dst > arc_n->dst)
196138fd1498Szrj 		    {
196238fd1498Szrj 		      changes = 1;
196338fd1498Szrj 		      if (arc_p)
196438fd1498Szrj 			arc_p->succ_next = arc_n;
196538fd1498Szrj 		      else
196638fd1498Szrj 			start = arc_n;
196738fd1498Szrj 		      arc->succ_next = arc_n->succ_next;
196838fd1498Szrj 		      arc_n->succ_next = arc;
196938fd1498Szrj 		      arc_p = arc_n;
197038fd1498Szrj 		    }
197138fd1498Szrj 		  else
197238fd1498Szrj 		    {
197338fd1498Szrj 		      arc_p = arc;
197438fd1498Szrj 		      arc = arc_n;
197538fd1498Szrj 		    }
197638fd1498Szrj 		}
197738fd1498Szrj 	    }
197838fd1498Szrj 	  blk->succ = start;
197938fd1498Szrj 	}
198038fd1498Szrj 
198138fd1498Szrj       /* Place it on the invalid chain, it will be ignored if that's
198238fd1498Szrj 	 wrong.  */
198338fd1498Szrj       blk->invalid_chain = 1;
198438fd1498Szrj       blk->chain = invalid_blocks;
198538fd1498Szrj       invalid_blocks = blk;
198638fd1498Szrj     }
198738fd1498Szrj 
198838fd1498Szrj   while (invalid_blocks || valid_blocks)
198938fd1498Szrj     {
199038fd1498Szrj       while ((blk = invalid_blocks))
199138fd1498Szrj 	{
199238fd1498Szrj 	  gcov_type total = 0;
199338fd1498Szrj 	  const arc_info *arc;
199438fd1498Szrj 
199538fd1498Szrj 	  invalid_blocks = blk->chain;
199638fd1498Szrj 	  blk->invalid_chain = 0;
199738fd1498Szrj 	  if (!blk->num_succ)
199838fd1498Szrj 	    for (arc = blk->succ; arc; arc = arc->succ_next)
199938fd1498Szrj 	      total += arc->count;
200038fd1498Szrj 	  else if (!blk->num_pred)
200138fd1498Szrj 	    for (arc = blk->pred; arc; arc = arc->pred_next)
200238fd1498Szrj 	      total += arc->count;
200338fd1498Szrj 	  else
200438fd1498Szrj 	    continue;
200538fd1498Szrj 
200638fd1498Szrj 	  blk->count = total;
200738fd1498Szrj 	  blk->count_valid = 1;
200838fd1498Szrj 	  blk->chain = valid_blocks;
200938fd1498Szrj 	  blk->valid_chain = 1;
201038fd1498Szrj 	  valid_blocks = blk;
201138fd1498Szrj 	}
201238fd1498Szrj       while ((blk = valid_blocks))
201338fd1498Szrj 	{
201438fd1498Szrj 	  gcov_type total;
201538fd1498Szrj 	  arc_info *arc, *inv_arc;
201638fd1498Szrj 
201738fd1498Szrj 	  valid_blocks = blk->chain;
201838fd1498Szrj 	  blk->valid_chain = 0;
201938fd1498Szrj 	  if (blk->num_succ == 1)
202038fd1498Szrj 	    {
202138fd1498Szrj 	      block_info *dst;
202238fd1498Szrj 
202338fd1498Szrj 	      total = blk->count;
202438fd1498Szrj 	      inv_arc = NULL;
202538fd1498Szrj 	      for (arc = blk->succ; arc; arc = arc->succ_next)
202638fd1498Szrj 		{
202738fd1498Szrj 		  total -= arc->count;
202838fd1498Szrj 		  if (!arc->count_valid)
202938fd1498Szrj 		    inv_arc = arc;
203038fd1498Szrj 		}
203138fd1498Szrj 	      dst = inv_arc->dst;
203238fd1498Szrj 	      inv_arc->count_valid = 1;
203338fd1498Szrj 	      inv_arc->count = total;
203438fd1498Szrj 	      blk->num_succ--;
203538fd1498Szrj 	      dst->num_pred--;
203638fd1498Szrj 	      if (dst->count_valid)
203738fd1498Szrj 		{
203838fd1498Szrj 		  if (dst->num_pred == 1 && !dst->valid_chain)
203938fd1498Szrj 		    {
204038fd1498Szrj 		      dst->chain = valid_blocks;
204138fd1498Szrj 		      dst->valid_chain = 1;
204238fd1498Szrj 		      valid_blocks = dst;
204338fd1498Szrj 		    }
204438fd1498Szrj 		}
204538fd1498Szrj 	      else
204638fd1498Szrj 		{
204738fd1498Szrj 		  if (!dst->num_pred && !dst->invalid_chain)
204838fd1498Szrj 		    {
204938fd1498Szrj 		      dst->chain = invalid_blocks;
205038fd1498Szrj 		      dst->invalid_chain = 1;
205138fd1498Szrj 		      invalid_blocks = dst;
205238fd1498Szrj 		    }
205338fd1498Szrj 		}
205438fd1498Szrj 	    }
205538fd1498Szrj 	  if (blk->num_pred == 1)
205638fd1498Szrj 	    {
205738fd1498Szrj 	      block_info *src;
205838fd1498Szrj 
205938fd1498Szrj 	      total = blk->count;
206038fd1498Szrj 	      inv_arc = NULL;
206138fd1498Szrj 	      for (arc = blk->pred; arc; arc = arc->pred_next)
206238fd1498Szrj 		{
206338fd1498Szrj 		  total -= arc->count;
206438fd1498Szrj 		  if (!arc->count_valid)
206538fd1498Szrj 		    inv_arc = arc;
206638fd1498Szrj 		}
206738fd1498Szrj 	      src = inv_arc->src;
206838fd1498Szrj 	      inv_arc->count_valid = 1;
206938fd1498Szrj 	      inv_arc->count = total;
207038fd1498Szrj 	      blk->num_pred--;
207138fd1498Szrj 	      src->num_succ--;
207238fd1498Szrj 	      if (src->count_valid)
207338fd1498Szrj 		{
207438fd1498Szrj 		  if (src->num_succ == 1 && !src->valid_chain)
207538fd1498Szrj 		    {
207638fd1498Szrj 		      src->chain = valid_blocks;
207738fd1498Szrj 		      src->valid_chain = 1;
207838fd1498Szrj 		      valid_blocks = src;
207938fd1498Szrj 		    }
208038fd1498Szrj 		}
208138fd1498Szrj 	      else
208238fd1498Szrj 		{
208338fd1498Szrj 		  if (!src->num_succ && !src->invalid_chain)
208438fd1498Szrj 		    {
208538fd1498Szrj 		      src->chain = invalid_blocks;
208638fd1498Szrj 		      src->invalid_chain = 1;
208738fd1498Szrj 		      invalid_blocks = src;
208838fd1498Szrj 		    }
208938fd1498Szrj 		}
209038fd1498Szrj 	    }
209138fd1498Szrj 	}
209238fd1498Szrj     }
209338fd1498Szrj 
209438fd1498Szrj   /* If the graph has been correctly solved, every block will have a
209538fd1498Szrj      valid count.  */
209638fd1498Szrj   for (unsigned i = 0; ix < fn->blocks.size (); i++)
209738fd1498Szrj     if (!fn->blocks[i].count_valid)
209838fd1498Szrj       {
209938fd1498Szrj 	fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
210038fd1498Szrj 		 bbg_file_name, fn->name);
210138fd1498Szrj 	break;
210238fd1498Szrj       }
210338fd1498Szrj }
210438fd1498Szrj 
210538fd1498Szrj /* Mark all the blocks only reachable via an incoming catch.  */
210638fd1498Szrj 
210738fd1498Szrj static void
find_exception_blocks(function_info * fn)210838fd1498Szrj find_exception_blocks (function_info *fn)
210938fd1498Szrj {
211038fd1498Szrj   unsigned ix;
211138fd1498Szrj   block_info **queue = XALLOCAVEC (block_info *, fn->blocks.size ());
211238fd1498Szrj 
211338fd1498Szrj   /* First mark all blocks as exceptional.  */
211438fd1498Szrj   for (ix = fn->blocks.size (); ix--;)
211538fd1498Szrj     fn->blocks[ix].exceptional = 1;
211638fd1498Szrj 
211738fd1498Szrj   /* Now mark all the blocks reachable via non-fake edges */
211838fd1498Szrj   queue[0] = &fn->blocks[0];
211938fd1498Szrj   queue[0]->exceptional = 0;
212038fd1498Szrj   for (ix = 1; ix;)
212138fd1498Szrj     {
212238fd1498Szrj       block_info *block = queue[--ix];
212338fd1498Szrj       const arc_info *arc;
212438fd1498Szrj 
212538fd1498Szrj       for (arc = block->succ; arc; arc = arc->succ_next)
212638fd1498Szrj 	if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
212738fd1498Szrj 	  {
212838fd1498Szrj 	    arc->dst->exceptional = 0;
212938fd1498Szrj 	    queue[ix++] = arc->dst;
213038fd1498Szrj 	  }
213138fd1498Szrj     }
213238fd1498Szrj }
213338fd1498Szrj 
213438fd1498Szrj 
213538fd1498Szrj /* Increment totals in COVERAGE according to arc ARC.  */
213638fd1498Szrj 
213738fd1498Szrj static void
add_branch_counts(coverage_info * coverage,const arc_info * arc)213838fd1498Szrj add_branch_counts (coverage_info *coverage, const arc_info *arc)
213938fd1498Szrj {
214038fd1498Szrj   if (arc->is_call_non_return)
214138fd1498Szrj     {
214238fd1498Szrj       coverage->calls++;
214338fd1498Szrj       if (arc->src->count)
214438fd1498Szrj 	coverage->calls_executed++;
214538fd1498Szrj     }
214638fd1498Szrj   else if (!arc->is_unconditional)
214738fd1498Szrj     {
214838fd1498Szrj       coverage->branches++;
214938fd1498Szrj       if (arc->src->count)
215038fd1498Szrj 	coverage->branches_executed++;
215138fd1498Szrj       if (arc->count)
215238fd1498Szrj 	coverage->branches_taken++;
215338fd1498Szrj     }
215438fd1498Szrj }
215538fd1498Szrj 
215638fd1498Szrj /* Format COUNT, if flag_human_readable_numbers is set, return it human
215738fd1498Szrj    readable format.  */
215838fd1498Szrj 
215938fd1498Szrj static char const *
format_count(gcov_type count)216038fd1498Szrj format_count (gcov_type count)
216138fd1498Szrj {
216238fd1498Szrj   static char buffer[64];
216338fd1498Szrj   const char *units = " kMGTPEZY";
216438fd1498Szrj 
216538fd1498Szrj   if (count < 1000 || !flag_human_readable_numbers)
216638fd1498Szrj     {
216738fd1498Szrj       sprintf (buffer, "%" PRId64, count);
216838fd1498Szrj       return buffer;
216938fd1498Szrj     }
217038fd1498Szrj 
217138fd1498Szrj   unsigned i;
217238fd1498Szrj   gcov_type divisor = 1;
217338fd1498Szrj   for (i = 0; units[i+1]; i++, divisor *= 1000)
217438fd1498Szrj     {
217538fd1498Szrj       if (count + divisor / 2 < 1000 * divisor)
217638fd1498Szrj 	break;
217738fd1498Szrj     }
217838fd1498Szrj   gcov_type r  = (count + divisor / 2) / divisor;
217938fd1498Szrj   sprintf (buffer, "%" PRId64 "%c", r, units[i]);
218038fd1498Szrj   return buffer;
218138fd1498Szrj }
218238fd1498Szrj 
218338fd1498Szrj /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
218438fd1498Szrj    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
218538fd1498Szrj    If DP is zero, no decimal point is printed. Only print 100% when
218638fd1498Szrj    TOP==BOTTOM and only print 0% when TOP=0.  If dp < 0, then simply
218738fd1498Szrj    format TOP.  Return pointer to a static string.  */
218838fd1498Szrj 
218938fd1498Szrj static char const *
format_gcov(gcov_type top,gcov_type bottom,int dp)219038fd1498Szrj format_gcov (gcov_type top, gcov_type bottom, int dp)
219138fd1498Szrj {
219238fd1498Szrj   static char buffer[20];
219338fd1498Szrj 
219438fd1498Szrj   /* Handle invalid values that would result in a misleading value.  */
219538fd1498Szrj   if (bottom != 0 && top > bottom && dp >= 0)
219638fd1498Szrj     {
219738fd1498Szrj       sprintf (buffer, "NAN %%");
219838fd1498Szrj       return buffer;
219938fd1498Szrj     }
220038fd1498Szrj 
220138fd1498Szrj   if (dp >= 0)
220238fd1498Szrj     {
220338fd1498Szrj       float ratio = bottom ? (float)top / bottom : 0;
220438fd1498Szrj       int ix;
220538fd1498Szrj       unsigned limit = 100;
220638fd1498Szrj       unsigned percent;
220738fd1498Szrj 
220838fd1498Szrj       for (ix = dp; ix--; )
220938fd1498Szrj 	limit *= 10;
221038fd1498Szrj 
221138fd1498Szrj       percent = (unsigned) (ratio * limit + (float)0.5);
221238fd1498Szrj       if (percent <= 0 && top)
221338fd1498Szrj 	percent = 1;
221438fd1498Szrj       else if (percent >= limit && top != bottom)
221538fd1498Szrj 	percent = limit - 1;
221638fd1498Szrj       ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
221738fd1498Szrj       if (dp)
221838fd1498Szrj 	{
221938fd1498Szrj 	  dp++;
222038fd1498Szrj 	  do
222138fd1498Szrj 	    {
222238fd1498Szrj 	      buffer[ix+1] = buffer[ix];
222338fd1498Szrj 	      ix--;
222438fd1498Szrj 	    }
222538fd1498Szrj 	  while (dp--);
222638fd1498Szrj 	  buffer[ix + 1] = '.';
222738fd1498Szrj 	}
222838fd1498Szrj     }
222938fd1498Szrj   else
223038fd1498Szrj     return format_count (top);
223138fd1498Szrj 
223238fd1498Szrj   return buffer;
223338fd1498Szrj }
223438fd1498Szrj 
223538fd1498Szrj /* Summary of execution */
223638fd1498Szrj 
223738fd1498Szrj static void
executed_summary(unsigned lines,unsigned executed)223838fd1498Szrj executed_summary (unsigned lines, unsigned executed)
223938fd1498Szrj {
224038fd1498Szrj   if (lines)
224138fd1498Szrj     fnotice (stdout, "Lines executed:%s of %d\n",
224238fd1498Szrj 	     format_gcov (executed, lines, 2), lines);
224338fd1498Szrj   else
224438fd1498Szrj     fnotice (stdout, "No executable lines\n");
224538fd1498Szrj }
224638fd1498Szrj 
224738fd1498Szrj /* Output summary info for a function or file.  */
224838fd1498Szrj 
224938fd1498Szrj static void
function_summary(const coverage_info * coverage,const char * title)225038fd1498Szrj function_summary (const coverage_info *coverage, const char *title)
225138fd1498Szrj {
225238fd1498Szrj   fnotice (stdout, "%s '%s'\n", title, coverage->name);
225338fd1498Szrj   executed_summary (coverage->lines, coverage->lines_executed);
225438fd1498Szrj 
225538fd1498Szrj   if (flag_branches)
225638fd1498Szrj     {
225738fd1498Szrj       if (coverage->branches)
225838fd1498Szrj 	{
225938fd1498Szrj 	  fnotice (stdout, "Branches executed:%s of %d\n",
226038fd1498Szrj 		   format_gcov (coverage->branches_executed,
226138fd1498Szrj 				coverage->branches, 2),
226238fd1498Szrj 		   coverage->branches);
226338fd1498Szrj 	  fnotice (stdout, "Taken at least once:%s of %d\n",
226438fd1498Szrj 		   format_gcov (coverage->branches_taken,
226538fd1498Szrj 				coverage->branches, 2),
226638fd1498Szrj 		   coverage->branches);
226738fd1498Szrj 	}
226838fd1498Szrj       else
226938fd1498Szrj 	fnotice (stdout, "No branches\n");
227038fd1498Szrj       if (coverage->calls)
227138fd1498Szrj 	fnotice (stdout, "Calls executed:%s of %d\n",
227238fd1498Szrj 		 format_gcov (coverage->calls_executed, coverage->calls, 2),
227338fd1498Szrj 		 coverage->calls);
227438fd1498Szrj       else
227538fd1498Szrj 	fnotice (stdout, "No calls\n");
227638fd1498Szrj     }
227738fd1498Szrj }
227838fd1498Szrj 
227938fd1498Szrj /* Canonicalize the filename NAME by canonicalizing directory
228038fd1498Szrj    separators, eliding . components and resolving .. components
228138fd1498Szrj    appropriately.  Always returns a unique string.  */
228238fd1498Szrj 
228338fd1498Szrj static char *
canonicalize_name(const char * name)228438fd1498Szrj canonicalize_name (const char *name)
228538fd1498Szrj {
228638fd1498Szrj   /* The canonical name cannot be longer than the incoming name.  */
228738fd1498Szrj   char *result = XNEWVEC (char, strlen (name) + 1);
228838fd1498Szrj   const char *base = name, *probe;
228938fd1498Szrj   char *ptr = result;
229038fd1498Szrj   char *dd_base;
229138fd1498Szrj   int slash = 0;
229238fd1498Szrj 
229338fd1498Szrj #if HAVE_DOS_BASED_FILE_SYSTEM
229438fd1498Szrj   if (base[0] && base[1] == ':')
229538fd1498Szrj     {
229638fd1498Szrj       result[0] = base[0];
229738fd1498Szrj       result[1] = ':';
229838fd1498Szrj       base += 2;
229938fd1498Szrj       ptr += 2;
230038fd1498Szrj     }
230138fd1498Szrj #endif
230238fd1498Szrj   for (dd_base = ptr; *base; base = probe)
230338fd1498Szrj     {
230438fd1498Szrj       size_t len;
230538fd1498Szrj 
230638fd1498Szrj       for (probe = base; *probe; probe++)
230738fd1498Szrj 	if (IS_DIR_SEPARATOR (*probe))
230838fd1498Szrj 	  break;
230938fd1498Szrj 
231038fd1498Szrj       len = probe - base;
231138fd1498Szrj       if (len == 1 && base[0] == '.')
231238fd1498Szrj 	/* Elide a '.' directory */
231338fd1498Szrj 	;
231438fd1498Szrj       else if (len == 2 && base[0] == '.' && base[1] == '.')
231538fd1498Szrj 	{
231638fd1498Szrj 	  /* '..', we can only elide it and the previous directory, if
231738fd1498Szrj 	     we're not a symlink.  */
231838fd1498Szrj 	  struct stat ATTRIBUTE_UNUSED buf;
231938fd1498Szrj 
232038fd1498Szrj 	  *ptr = 0;
232138fd1498Szrj 	  if (dd_base == ptr
232238fd1498Szrj #if defined (S_ISLNK)
232338fd1498Szrj 	      /* S_ISLNK is not POSIX.1-1996.  */
232438fd1498Szrj 	      || stat (result, &buf) || S_ISLNK (buf.st_mode)
232538fd1498Szrj #endif
232638fd1498Szrj 		)
232738fd1498Szrj 	    {
232838fd1498Szrj 	      /* Cannot elide, or unreadable or a symlink.  */
232938fd1498Szrj 	      dd_base = ptr + 2 + slash;
233038fd1498Szrj 	      goto regular;
233138fd1498Szrj 	    }
233238fd1498Szrj 	  while (ptr != dd_base && *ptr != '/')
233338fd1498Szrj 	    ptr--;
233438fd1498Szrj 	  slash = ptr != result;
233538fd1498Szrj 	}
233638fd1498Szrj       else
233738fd1498Szrj 	{
233838fd1498Szrj 	regular:
233938fd1498Szrj 	  /* Regular pathname component.  */
234038fd1498Szrj 	  if (slash)
234138fd1498Szrj 	    *ptr++ = '/';
234238fd1498Szrj 	  memcpy (ptr, base, len);
234338fd1498Szrj 	  ptr += len;
234438fd1498Szrj 	  slash = 1;
234538fd1498Szrj 	}
234638fd1498Szrj 
234738fd1498Szrj       for (; IS_DIR_SEPARATOR (*probe); probe++)
234838fd1498Szrj 	continue;
234938fd1498Szrj     }
235038fd1498Szrj   *ptr = 0;
235138fd1498Szrj 
235238fd1498Szrj   return result;
235338fd1498Szrj }
235438fd1498Szrj 
235538fd1498Szrj /* Print hex representation of 16 bytes from SUM and write it to BUFFER.  */
235638fd1498Szrj 
235738fd1498Szrj static void
md5sum_to_hex(const char * sum,char * buffer)235838fd1498Szrj md5sum_to_hex (const char *sum, char *buffer)
235938fd1498Szrj {
236038fd1498Szrj   for (unsigned i = 0; i < 16; i++)
236138fd1498Szrj     sprintf (buffer + (2 * i), "%02x", (unsigned char)sum[i]);
236238fd1498Szrj }
236338fd1498Szrj 
236438fd1498Szrj /* Generate an output file name. INPUT_NAME is the canonicalized main
236538fd1498Szrj    input file and SRC_NAME is the canonicalized file name.
236638fd1498Szrj    LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation.  With
236738fd1498Szrj    long_output_names we prepend the processed name of the input file
236838fd1498Szrj    to each output name (except when the current source file is the
236938fd1498Szrj    input file, so you don't get a double concatenation). The two
237038fd1498Szrj    components are separated by '##'.  With preserve_paths we create a
237138fd1498Szrj    filename from all path components of the source file, replacing '/'
237238fd1498Szrj    with '#', and .. with '^', without it we simply take the basename
237338fd1498Szrj    component.  (Remember, the canonicalized name will already have
237438fd1498Szrj    elided '.' components and converted \\ separators.)  */
237538fd1498Szrj 
237638fd1498Szrj static char *
make_gcov_file_name(const char * input_name,const char * src_name)237738fd1498Szrj make_gcov_file_name (const char *input_name, const char *src_name)
237838fd1498Szrj {
237938fd1498Szrj   char *ptr;
238038fd1498Szrj   char *result;
238138fd1498Szrj 
238238fd1498Szrj   if (flag_long_names && input_name && strcmp (src_name, input_name))
238338fd1498Szrj     {
238438fd1498Szrj       /* Generate the input filename part.  */
238538fd1498Szrj       result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
238638fd1498Szrj 
238738fd1498Szrj       ptr = result;
238838fd1498Szrj       ptr = mangle_name (input_name, ptr);
238938fd1498Szrj       ptr[0] = ptr[1] = '#';
239038fd1498Szrj       ptr += 2;
239138fd1498Szrj     }
239238fd1498Szrj   else
239338fd1498Szrj     {
239438fd1498Szrj       result = XNEWVEC (char, strlen (src_name) + 10);
239538fd1498Szrj       ptr = result;
239638fd1498Szrj     }
239738fd1498Szrj 
239838fd1498Szrj   ptr = mangle_name (src_name, ptr);
239938fd1498Szrj   strcpy (ptr, ".gcov");
240038fd1498Szrj 
240138fd1498Szrj   /* When hashing filenames, we shorten them by only using the filename
240238fd1498Szrj      component and appending a hash of the full (mangled) pathname.  */
240338fd1498Szrj   if (flag_hash_filenames)
240438fd1498Szrj     {
240538fd1498Szrj       md5_ctx ctx;
240638fd1498Szrj       char md5sum[16];
240738fd1498Szrj       char md5sum_hex[33];
240838fd1498Szrj 
240938fd1498Szrj       md5_init_ctx (&ctx);
241038fd1498Szrj       md5_process_bytes (src_name, strlen (src_name), &ctx);
241138fd1498Szrj       md5_finish_ctx (&ctx, md5sum);
241238fd1498Szrj       md5sum_to_hex (md5sum, md5sum_hex);
241338fd1498Szrj       free (result);
241438fd1498Szrj 
241538fd1498Szrj       result = XNEWVEC (char, strlen (src_name) + 50);
241638fd1498Szrj       ptr = result;
241738fd1498Szrj       ptr = mangle_name (src_name, ptr);
241838fd1498Szrj       ptr[0] = ptr[1] = '#';
241938fd1498Szrj       ptr += 2;
242038fd1498Szrj       memcpy (ptr, md5sum_hex, 32);
242138fd1498Szrj       ptr += 32;
242238fd1498Szrj       strcpy (ptr, ".gcov");
242338fd1498Szrj     }
242438fd1498Szrj 
242538fd1498Szrj   return result;
242638fd1498Szrj }
242738fd1498Szrj 
242838fd1498Szrj static char *
mangle_name(char const * base,char * ptr)242938fd1498Szrj mangle_name (char const *base, char *ptr)
243038fd1498Szrj {
243138fd1498Szrj   size_t len;
243238fd1498Szrj 
243338fd1498Szrj   /* Generate the source filename part.  */
243438fd1498Szrj   if (!flag_preserve_paths)
243538fd1498Szrj     {
243638fd1498Szrj       base = lbasename (base);
243738fd1498Szrj       len = strlen (base);
243838fd1498Szrj       memcpy (ptr, base, len);
243938fd1498Szrj       ptr += len;
244038fd1498Szrj     }
244138fd1498Szrj   else
244238fd1498Szrj     {
244338fd1498Szrj       /* Convert '/' to '#', convert '..' to '^',
244438fd1498Szrj 	 convert ':' to '~' on DOS based file system.  */
244538fd1498Szrj       const char *probe;
244638fd1498Szrj 
244738fd1498Szrj #if HAVE_DOS_BASED_FILE_SYSTEM
244838fd1498Szrj       if (base[0] && base[1] == ':')
244938fd1498Szrj 	{
245038fd1498Szrj 	  ptr[0] = base[0];
245138fd1498Szrj 	  ptr[1] = '~';
245238fd1498Szrj 	  ptr += 2;
245338fd1498Szrj 	  base += 2;
245438fd1498Szrj 	}
245538fd1498Szrj #endif
245638fd1498Szrj       for (; *base; base = probe)
245738fd1498Szrj 	{
245838fd1498Szrj 	  size_t len;
245938fd1498Szrj 
246038fd1498Szrj 	  for (probe = base; *probe; probe++)
246138fd1498Szrj 	    if (*probe == '/')
246238fd1498Szrj 	      break;
246338fd1498Szrj 	  len = probe - base;
246438fd1498Szrj 	  if (len == 2 && base[0] == '.' && base[1] == '.')
246538fd1498Szrj 	    *ptr++ = '^';
246638fd1498Szrj 	  else
246738fd1498Szrj 	    {
246838fd1498Szrj 	      memcpy (ptr, base, len);
246938fd1498Szrj 	      ptr += len;
247038fd1498Szrj 	    }
247138fd1498Szrj 	  if (*probe)
247238fd1498Szrj 	    {
247338fd1498Szrj 	      *ptr++ = '#';
247438fd1498Szrj 	      probe++;
247538fd1498Szrj 	    }
247638fd1498Szrj 	}
247738fd1498Szrj     }
247838fd1498Szrj 
247938fd1498Szrj   return ptr;
248038fd1498Szrj }
248138fd1498Szrj 
248238fd1498Szrj /* Scan through the bb_data for each line in the block, increment
248338fd1498Szrj    the line number execution count indicated by the execution count of
248438fd1498Szrj    the appropriate basic block.  */
248538fd1498Szrj 
248638fd1498Szrj static void
add_line_counts(coverage_info * coverage,function_info * fn)248738fd1498Szrj add_line_counts (coverage_info *coverage, function_info *fn)
248838fd1498Szrj {
248938fd1498Szrj   bool has_any_line = false;
249038fd1498Szrj   /* Scan each basic block.  */
249138fd1498Szrj   for (unsigned ix = 0; ix != fn->blocks.size (); ix++)
249238fd1498Szrj     {
249338fd1498Szrj       line_info *line = NULL;
249438fd1498Szrj       block_info *block = &fn->blocks[ix];
249538fd1498Szrj       if (block->count && ix && ix + 1 != fn->blocks.size ())
249638fd1498Szrj 	fn->blocks_executed++;
249738fd1498Szrj       for (unsigned i = 0; i < block->locations.size (); i++)
249838fd1498Szrj 	{
249938fd1498Szrj 	  unsigned src_idx = block->locations[i].source_file_idx;
250038fd1498Szrj 	  vector<unsigned> &lines = block->locations[i].lines;
250138fd1498Szrj 
250238fd1498Szrj 	  block->cycle.arc = NULL;
250338fd1498Szrj 	  block->cycle.ident = ~0U;
250438fd1498Szrj 
250538fd1498Szrj 	  for (unsigned j = 0; j < lines.size (); j++)
250638fd1498Szrj 	    {
250738fd1498Szrj 	      unsigned ln = lines[j];
250838fd1498Szrj 
250938fd1498Szrj 	      /* Line belongs to a function that is in a group.  */
251038fd1498Szrj 	      if (fn->group_line_p (ln, src_idx))
251138fd1498Szrj 		{
251238fd1498Szrj 		  gcc_assert (lines[j] - fn->start_line < fn->lines.size ());
251338fd1498Szrj 		  line = &(fn->lines[lines[j] - fn->start_line]);
251438fd1498Szrj 		  line->exists = 1;
251538fd1498Szrj 		  if (!block->exceptional)
251638fd1498Szrj 		    {
251738fd1498Szrj 		      line->unexceptional = 1;
251838fd1498Szrj 		      if (block->count == 0)
251938fd1498Szrj 			line->has_unexecuted_block = 1;
252038fd1498Szrj 		    }
252138fd1498Szrj 		  line->count += block->count;
252238fd1498Szrj 		}
252338fd1498Szrj 	      else
252438fd1498Szrj 		{
252538fd1498Szrj 		  gcc_assert (ln < sources[src_idx].lines.size ());
252638fd1498Szrj 		  line = &(sources[src_idx].lines[ln]);
252738fd1498Szrj 		  if (coverage)
252838fd1498Szrj 		    {
252938fd1498Szrj 		      if (!line->exists)
253038fd1498Szrj 			coverage->lines++;
253138fd1498Szrj 		      if (!line->count && block->count)
253238fd1498Szrj 			coverage->lines_executed++;
253338fd1498Szrj 		    }
253438fd1498Szrj 		  line->exists = 1;
253538fd1498Szrj 		  if (!block->exceptional)
253638fd1498Szrj 		    {
253738fd1498Szrj 		      line->unexceptional = 1;
253838fd1498Szrj 		      if (block->count == 0)
253938fd1498Szrj 			line->has_unexecuted_block = 1;
254038fd1498Szrj 		    }
254138fd1498Szrj 		  line->count += block->count;
254238fd1498Szrj 		}
254338fd1498Szrj 	    }
254438fd1498Szrj 
254538fd1498Szrj 	  has_any_line = true;
254638fd1498Szrj 
254738fd1498Szrj 	  if (!ix || ix + 1 == fn->blocks.size ())
254838fd1498Szrj 	    /* Entry or exit block.  */;
254938fd1498Szrj 	  else if (line != NULL)
255038fd1498Szrj 	    {
255138fd1498Szrj 	      line->blocks.push_back (block);
255238fd1498Szrj 
255338fd1498Szrj 	      if (flag_branches)
255438fd1498Szrj 		{
255538fd1498Szrj 		  arc_info *arc;
255638fd1498Szrj 
255738fd1498Szrj 		  for (arc = block->succ; arc; arc = arc->succ_next)
255838fd1498Szrj 		    line->branches.push_back (arc);
255938fd1498Szrj 		}
256038fd1498Szrj 	    }
256138fd1498Szrj 	}
256238fd1498Szrj     }
256338fd1498Szrj 
256438fd1498Szrj   if (!has_any_line)
256538fd1498Szrj     fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
256638fd1498Szrj }
256738fd1498Szrj 
256838fd1498Szrj /* Accumulate info for LINE that belongs to SRC source file.  If ADD_COVERAGE
256938fd1498Szrj    is set to true, update source file summary.  */
257038fd1498Szrj 
accumulate_line_info(line_info * line,source_info * src,bool add_coverage)257138fd1498Szrj static void accumulate_line_info (line_info *line, source_info *src,
257238fd1498Szrj 				  bool add_coverage)
257338fd1498Szrj {
257438fd1498Szrj   if (add_coverage)
257538fd1498Szrj     for (vector<arc_info *>::iterator it = line->branches.begin ();
257638fd1498Szrj 	 it != line->branches.end (); it++)
257738fd1498Szrj       add_branch_counts (&src->coverage, *it);
257838fd1498Szrj 
257938fd1498Szrj   if (!line->blocks.empty ())
258038fd1498Szrj     {
258138fd1498Szrj       /* The user expects the line count to be the number of times
258238fd1498Szrj 	 a line has been executed.  Simply summing the block count
258338fd1498Szrj 	 will give an artificially high number.  The Right Thing
258438fd1498Szrj 	 is to sum the entry counts to the graph of blocks on this
258538fd1498Szrj 	 line, then find the elementary cycles of the local graph
258638fd1498Szrj 	 and add the transition counts of those cycles.  */
258738fd1498Szrj       gcov_type count = 0;
258838fd1498Szrj 
258938fd1498Szrj       /* Cycle detection.  */
259038fd1498Szrj       for (vector<block_info *>::iterator it = line->blocks.begin ();
259138fd1498Szrj 	   it != line->blocks.end (); it++)
259238fd1498Szrj 	{
259338fd1498Szrj 	  for (arc_info *arc = (*it)->pred; arc; arc = arc->pred_next)
259438fd1498Szrj 	    if (!line->has_block (arc->src))
259538fd1498Szrj 	      count += arc->count;
259638fd1498Szrj 	  for (arc_info *arc = (*it)->succ; arc; arc = arc->succ_next)
259738fd1498Szrj 	    arc->cs_count = arc->count;
259838fd1498Szrj 	}
259938fd1498Szrj 
260038fd1498Szrj       /* Now, add the count of loops entirely on this line.  */
260138fd1498Szrj       count += get_cycles_count (*line);
260238fd1498Szrj       line->count = count;
260338fd1498Szrj     }
260438fd1498Szrj 
260538fd1498Szrj   if (line->exists && add_coverage)
260638fd1498Szrj     {
260738fd1498Szrj       src->coverage.lines++;
260838fd1498Szrj       if (line->count)
260938fd1498Szrj 	src->coverage.lines_executed++;
261038fd1498Szrj     }
261138fd1498Szrj }
261238fd1498Szrj 
261338fd1498Szrj /* Accumulate the line counts of a file.  */
261438fd1498Szrj 
261538fd1498Szrj static void
accumulate_line_counts(source_info * src)261638fd1498Szrj accumulate_line_counts (source_info *src)
261738fd1498Szrj {
261838fd1498Szrj   /* First work on group functions.  */
261938fd1498Szrj   for (vector<function_info *>::iterator it = src->functions.begin ();
262038fd1498Szrj        it != src->functions.end (); it++)
262138fd1498Szrj     {
262238fd1498Szrj       function_info *fn = *it;
262338fd1498Szrj 
262438fd1498Szrj       if (fn->src != src->index || !fn->is_group)
262538fd1498Szrj 	continue;
262638fd1498Szrj 
262738fd1498Szrj       for (vector<line_info>::iterator it2 = fn->lines.begin ();
262838fd1498Szrj 	   it2 != fn->lines.end (); it2++)
262938fd1498Szrj 	  {
263038fd1498Szrj 	    line_info *line = &(*it2);
263138fd1498Szrj 	    accumulate_line_info (line, src, false);
263238fd1498Szrj 	  }
263338fd1498Szrj     }
263438fd1498Szrj 
263538fd1498Szrj   /* Work on global lines that line in source file SRC.  */
263638fd1498Szrj   for (vector<line_info>::iterator it = src->lines.begin ();
263738fd1498Szrj        it != src->lines.end (); it++)
263838fd1498Szrj     accumulate_line_info (&(*it), src, true);
263938fd1498Szrj 
264038fd1498Szrj   /* If not using intermediate mode, sum lines of group functions and
264138fd1498Szrj      add them to lines that live in a source file.  */
264238fd1498Szrj   if (!flag_intermediate_format)
264338fd1498Szrj     for (vector<function_info *>::iterator it = src->functions.begin ();
264438fd1498Szrj 	 it != src->functions.end (); it++)
264538fd1498Szrj       {
264638fd1498Szrj 	function_info *fn = *it;
264738fd1498Szrj 
264838fd1498Szrj 	if (fn->src != src->index || !fn->is_group)
264938fd1498Szrj 	  continue;
265038fd1498Szrj 
265138fd1498Szrj 	for (unsigned i = 0; i < fn->lines.size (); i++)
265238fd1498Szrj 	  {
265338fd1498Szrj 	    line_info *fn_line = &fn->lines[i];
265438fd1498Szrj 	    if (fn_line->exists)
265538fd1498Szrj 	      {
265638fd1498Szrj 		unsigned ln = fn->start_line + i;
265738fd1498Szrj 		line_info *src_line = &src->lines[ln];
265838fd1498Szrj 
265938fd1498Szrj 		if (!src_line->exists)
266038fd1498Szrj 		  src->coverage.lines++;
266138fd1498Szrj 		if (!src_line->count && fn_line->count)
266238fd1498Szrj 		  src->coverage.lines_executed++;
266338fd1498Szrj 
266438fd1498Szrj 		src_line->count += fn_line->count;
266538fd1498Szrj 		src_line->exists = 1;
266638fd1498Szrj 
266738fd1498Szrj 		if (fn_line->has_unexecuted_block)
266838fd1498Szrj 		  src_line->has_unexecuted_block = 1;
266938fd1498Szrj 
267038fd1498Szrj 		if (fn_line->unexceptional)
267138fd1498Szrj 		  src_line->unexceptional = 1;
267238fd1498Szrj 	      }
267338fd1498Szrj 	  }
267438fd1498Szrj       }
267538fd1498Szrj }
267638fd1498Szrj 
267738fd1498Szrj /* Output information about ARC number IX.  Returns nonzero if
267838fd1498Szrj    anything is output.  */
267938fd1498Szrj 
268038fd1498Szrj static int
output_branch_count(FILE * gcov_file,int ix,const arc_info * arc)268138fd1498Szrj output_branch_count (FILE *gcov_file, int ix, const arc_info *arc)
268238fd1498Szrj {
268338fd1498Szrj   if (arc->is_call_non_return)
268438fd1498Szrj     {
268538fd1498Szrj       if (arc->src->count)
268638fd1498Szrj 	{
268738fd1498Szrj 	  fnotice (gcov_file, "call   %2d returned %s\n", ix,
268838fd1498Szrj 		   format_gcov (arc->src->count - arc->count,
268938fd1498Szrj 				arc->src->count, -flag_counts));
269038fd1498Szrj 	}
269138fd1498Szrj       else
269238fd1498Szrj 	fnotice (gcov_file, "call   %2d never executed\n", ix);
269338fd1498Szrj     }
269438fd1498Szrj   else if (!arc->is_unconditional)
269538fd1498Szrj     {
269638fd1498Szrj       if (arc->src->count)
269738fd1498Szrj 	fnotice (gcov_file, "branch %2d taken %s%s", ix,
269838fd1498Szrj 		 format_gcov (arc->count, arc->src->count, -flag_counts),
269938fd1498Szrj 		 arc->fall_through ? " (fallthrough)"
270038fd1498Szrj 		 : arc->is_throw ? " (throw)" : "");
270138fd1498Szrj       else
270238fd1498Szrj 	fnotice (gcov_file, "branch %2d never executed", ix);
270338fd1498Szrj 
270438fd1498Szrj       if (flag_verbose)
270538fd1498Szrj 	fnotice (gcov_file, " (BB %d)", arc->dst->id);
270638fd1498Szrj 
270738fd1498Szrj       fnotice (gcov_file, "\n");
270838fd1498Szrj     }
270938fd1498Szrj   else if (flag_unconditional && !arc->dst->is_call_return)
271038fd1498Szrj     {
271138fd1498Szrj       if (arc->src->count)
271238fd1498Szrj 	fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
271338fd1498Szrj 		 format_gcov (arc->count, arc->src->count, -flag_counts));
271438fd1498Szrj       else
271538fd1498Szrj 	fnotice (gcov_file, "unconditional %2d never executed\n", ix);
271638fd1498Szrj     }
271738fd1498Szrj   else
271838fd1498Szrj     return 0;
271938fd1498Szrj   return 1;
272038fd1498Szrj }
272138fd1498Szrj 
272238fd1498Szrj static const char *
read_line(FILE * file)272338fd1498Szrj read_line (FILE *file)
272438fd1498Szrj {
272538fd1498Szrj   static char *string;
272638fd1498Szrj   static size_t string_len;
272738fd1498Szrj   size_t pos = 0;
272838fd1498Szrj   char *ptr;
272938fd1498Szrj 
273038fd1498Szrj   if (!string_len)
273138fd1498Szrj     {
273238fd1498Szrj       string_len = 200;
273338fd1498Szrj       string = XNEWVEC (char, string_len);
273438fd1498Szrj     }
273538fd1498Szrj 
273638fd1498Szrj   while ((ptr = fgets (string + pos, string_len - pos, file)))
273738fd1498Szrj     {
273838fd1498Szrj       size_t len = strlen (string + pos);
273938fd1498Szrj 
274038fd1498Szrj       if (len && string[pos + len - 1] == '\n')
274138fd1498Szrj 	{
274238fd1498Szrj 	  string[pos + len - 1] = 0;
274338fd1498Szrj 	  return string;
274438fd1498Szrj 	}
274538fd1498Szrj       pos += len;
274638fd1498Szrj       /* If the file contains NUL characters or an incomplete
274738fd1498Szrj 	 last line, which can happen more than once in one run,
274838fd1498Szrj 	 we have to avoid doubling the STRING_LEN unnecessarily.  */
274938fd1498Szrj       if (pos > string_len / 2)
275038fd1498Szrj 	{
275138fd1498Szrj 	  string_len *= 2;
275238fd1498Szrj 	  string = XRESIZEVEC (char, string, string_len);
275338fd1498Szrj 	}
275438fd1498Szrj     }
275538fd1498Szrj 
275638fd1498Szrj   return pos ? string : NULL;
275738fd1498Szrj }
275838fd1498Szrj 
275938fd1498Szrj /* Pad string S with spaces from left to have total width equal to 9.  */
276038fd1498Szrj 
276138fd1498Szrj static void
pad_count_string(string & s)276238fd1498Szrj pad_count_string (string &s)
276338fd1498Szrj {
276438fd1498Szrj   if (s.size () < 9)
276538fd1498Szrj     s.insert (0, 9 - s.size (), ' ');
276638fd1498Szrj }
276738fd1498Szrj 
276838fd1498Szrj /* Print GCOV line beginning to F stream.  If EXISTS is set to true, the
276938fd1498Szrj    line exists in source file.  UNEXCEPTIONAL indicated that it's not in
277038fd1498Szrj    an exceptional statement.  The output is printed for LINE_NUM of given
277138fd1498Szrj    COUNT of executions.  EXCEPTIONAL_STRING and UNEXCEPTIONAL_STRING are
277238fd1498Szrj    used to indicate non-executed blocks.  */
277338fd1498Szrj 
277438fd1498Szrj static void
output_line_beginning(FILE * f,bool exists,bool unexceptional,bool has_unexecuted_block,gcov_type count,unsigned line_num,const char * exceptional_string,const char * unexceptional_string)277538fd1498Szrj output_line_beginning (FILE *f, bool exists, bool unexceptional,
277638fd1498Szrj 		       bool has_unexecuted_block,
277738fd1498Szrj 		       gcov_type count, unsigned line_num,
277838fd1498Szrj 		       const char *exceptional_string,
277938fd1498Szrj 		       const char *unexceptional_string)
278038fd1498Szrj {
278138fd1498Szrj   string s;
278238fd1498Szrj   if (exists)
278338fd1498Szrj     {
278438fd1498Szrj       if (count > 0)
278538fd1498Szrj 	{
278638fd1498Szrj 	  s = format_gcov (count, 0, -1);
278738fd1498Szrj 	  if (has_unexecuted_block
278838fd1498Szrj 	      && bbg_supports_has_unexecuted_blocks)
278938fd1498Szrj 	    {
279038fd1498Szrj 	      if (flag_use_colors)
279138fd1498Szrj 		{
279238fd1498Szrj 		  pad_count_string (s);
279338fd1498Szrj 		  s.insert (0, SGR_SEQ (COLOR_BG_MAGENTA
279438fd1498Szrj 					COLOR_SEPARATOR COLOR_FG_WHITE));
279538fd1498Szrj 		  s += SGR_RESET;
279638fd1498Szrj 		}
279738fd1498Szrj 	      else
279838fd1498Szrj 		s += "*";
279938fd1498Szrj 	    }
280038fd1498Szrj 	  pad_count_string (s);
280138fd1498Szrj 	}
280238fd1498Szrj       else
280338fd1498Szrj 	{
280438fd1498Szrj 	  if (flag_use_colors)
280538fd1498Szrj 	    {
280638fd1498Szrj 	      s = "0";
280738fd1498Szrj 	      pad_count_string (s);
280838fd1498Szrj 	      if (unexceptional)
280938fd1498Szrj 		s.insert (0, SGR_SEQ (COLOR_BG_RED
281038fd1498Szrj 				      COLOR_SEPARATOR COLOR_FG_WHITE));
281138fd1498Szrj 	      else
281238fd1498Szrj 		s.insert (0, SGR_SEQ (COLOR_BG_CYAN
281338fd1498Szrj 				      COLOR_SEPARATOR COLOR_FG_WHITE));
281438fd1498Szrj 	      s += SGR_RESET;
281538fd1498Szrj 	    }
281638fd1498Szrj 	  else
281738fd1498Szrj 	    {
281838fd1498Szrj 	      s = unexceptional ? unexceptional_string : exceptional_string;
281938fd1498Szrj 	      pad_count_string (s);
282038fd1498Szrj 	    }
282138fd1498Szrj 	}
282238fd1498Szrj     }
282338fd1498Szrj   else
282438fd1498Szrj     {
282538fd1498Szrj       s = "-";
282638fd1498Szrj       pad_count_string (s);
282738fd1498Szrj     }
282838fd1498Szrj 
282938fd1498Szrj   fprintf (f, "%s:%5u", s.c_str (), line_num);
283038fd1498Szrj }
283138fd1498Szrj 
283238fd1498Szrj static void
print_source_line(FILE * f,const vector<const char * > & source_lines,unsigned line)283338fd1498Szrj print_source_line (FILE *f, const vector<const char *> &source_lines,
283438fd1498Szrj 		   unsigned line)
283538fd1498Szrj {
283638fd1498Szrj   gcc_assert (line >= 1);
283738fd1498Szrj   gcc_assert (line <= source_lines.size ());
283838fd1498Szrj 
283938fd1498Szrj   fprintf (f, ":%s\n", source_lines[line - 1]);
284038fd1498Szrj }
284138fd1498Szrj 
284238fd1498Szrj /* Output line details for LINE and print it to F file.  LINE lives on
284338fd1498Szrj    LINE_NUM.  */
284438fd1498Szrj 
284538fd1498Szrj static void
output_line_details(FILE * f,const line_info * line,unsigned line_num)284638fd1498Szrj output_line_details (FILE *f, const line_info *line, unsigned line_num)
284738fd1498Szrj {
284838fd1498Szrj   if (flag_all_blocks)
284938fd1498Szrj     {
285038fd1498Szrj       arc_info *arc;
285138fd1498Szrj       int ix, jx;
285238fd1498Szrj 
285338fd1498Szrj       ix = jx = 0;
285438fd1498Szrj       for (vector<block_info *>::const_iterator it = line->blocks.begin ();
285538fd1498Szrj 	   it != line->blocks.end (); it++)
285638fd1498Szrj 	{
285738fd1498Szrj 	  if (!(*it)->is_call_return)
285838fd1498Szrj 	    {
285938fd1498Szrj 	      output_line_beginning (f, line->exists,
286038fd1498Szrj 				     (*it)->exceptional, false,
286138fd1498Szrj 				     (*it)->count, line_num,
286238fd1498Szrj 				     "%%%%%", "$$$$$");
286338fd1498Szrj 	      fprintf (f, "-block %2d", ix++);
286438fd1498Szrj 	      if (flag_verbose)
286538fd1498Szrj 		fprintf (f, " (BB %u)", (*it)->id);
286638fd1498Szrj 	      fprintf (f, "\n");
286738fd1498Szrj 	    }
286838fd1498Szrj 	  if (flag_branches)
286938fd1498Szrj 	    for (arc = (*it)->succ; arc; arc = arc->succ_next)
287038fd1498Szrj 	      jx += output_branch_count (f, jx, arc);
287138fd1498Szrj 	}
287238fd1498Szrj     }
287338fd1498Szrj   else if (flag_branches)
287438fd1498Szrj     {
287538fd1498Szrj       int ix;
287638fd1498Szrj 
287738fd1498Szrj       ix = 0;
287838fd1498Szrj       for (vector<arc_info *>::const_iterator it = line->branches.begin ();
287938fd1498Szrj 	   it != line->branches.end (); it++)
288038fd1498Szrj 	ix += output_branch_count (f, ix, (*it));
288138fd1498Szrj     }
288238fd1498Szrj }
288338fd1498Szrj 
288438fd1498Szrj /* Output detail statistics about function FN to file F.  */
288538fd1498Szrj 
288638fd1498Szrj static void
output_function_details(FILE * f,const function_info * fn)288738fd1498Szrj output_function_details (FILE *f, const function_info *fn)
288838fd1498Szrj {
288938fd1498Szrj   if (!flag_branches)
289038fd1498Szrj     return;
289138fd1498Szrj 
289238fd1498Szrj   arc_info *arc = fn->blocks[EXIT_BLOCK].pred;
289338fd1498Szrj   gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
289438fd1498Szrj   gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
289538fd1498Szrj 
289638fd1498Szrj   for (; arc; arc = arc->pred_next)
289738fd1498Szrj     if (arc->fake)
289838fd1498Szrj       return_count -= arc->count;
289938fd1498Szrj 
290038fd1498Szrj   fprintf (f, "function %s",
290138fd1498Szrj 	   flag_demangled_names ? fn->demangled_name : fn->name);
290238fd1498Szrj   fprintf (f, " called %s",
290338fd1498Szrj 	   format_gcov (called_count, 0, -1));
290438fd1498Szrj   fprintf (f, " returned %s",
290538fd1498Szrj 	   format_gcov (return_count, called_count, 0));
290638fd1498Szrj   fprintf (f, " blocks executed %s",
290738fd1498Szrj 	   format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
290838fd1498Szrj 			0));
290938fd1498Szrj   fprintf (f, "\n");
291038fd1498Szrj }
291138fd1498Szrj 
291238fd1498Szrj /* Read in the source file one line at a time, and output that line to
291338fd1498Szrj    the gcov file preceded by its execution count and other
291438fd1498Szrj    information.  */
291538fd1498Szrj 
291638fd1498Szrj static void
output_lines(FILE * gcov_file,const source_info * src)291738fd1498Szrj output_lines (FILE *gcov_file, const source_info *src)
291838fd1498Szrj {
291938fd1498Szrj #define  DEFAULT_LINE_START "        -:    0:"
292038fd1498Szrj #define FN_SEPARATOR "------------------\n"
292138fd1498Szrj 
292238fd1498Szrj   FILE *source_file;
292338fd1498Szrj   const char *retval;
292438fd1498Szrj 
292538fd1498Szrj   fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
292638fd1498Szrj   if (!multiple_files)
292738fd1498Szrj     {
292838fd1498Szrj       fprintf (gcov_file, DEFAULT_LINE_START "Graph:%s\n", bbg_file_name);
292938fd1498Szrj       fprintf (gcov_file, DEFAULT_LINE_START "Data:%s\n",
293038fd1498Szrj 	       no_data_file ? "-" : da_file_name);
293138fd1498Szrj       fprintf (gcov_file, DEFAULT_LINE_START "Runs:%u\n", object_runs);
293238fd1498Szrj     }
293338fd1498Szrj   fprintf (gcov_file, DEFAULT_LINE_START "Programs:%u\n", program_count);
293438fd1498Szrj 
293538fd1498Szrj   source_file = fopen (src->name, "r");
293638fd1498Szrj   if (!source_file)
293738fd1498Szrj     fnotice (stderr, "Cannot open source file %s\n", src->name);
293838fd1498Szrj   else if (src->file_time == 0)
293938fd1498Szrj     fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
294038fd1498Szrj 
294138fd1498Szrj   vector<const char *> source_lines;
294238fd1498Szrj   if (source_file)
294338fd1498Szrj     while ((retval = read_line (source_file)) != NULL)
294438fd1498Szrj       source_lines.push_back (xstrdup (retval));
294538fd1498Szrj 
294638fd1498Szrj   unsigned line_start_group = 0;
294738fd1498Szrj   vector<function_info *> fns;
294838fd1498Szrj 
294938fd1498Szrj   for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
295038fd1498Szrj     {
295138fd1498Szrj       if (line_num >= src->lines.size ())
295238fd1498Szrj 	{
295338fd1498Szrj 	  fprintf (gcov_file, "%9s:%5u", "-", line_num);
295438fd1498Szrj 	  print_source_line (gcov_file, source_lines, line_num);
295538fd1498Szrj 	  continue;
295638fd1498Szrj 	}
295738fd1498Szrj 
295838fd1498Szrj       const line_info *line = &src->lines[line_num];
295938fd1498Szrj 
296038fd1498Szrj       if (line_start_group == 0)
296138fd1498Szrj 	{
296238fd1498Szrj 	  fns = src->get_functions_at_location (line_num);
296338fd1498Szrj 	  if (fns.size () > 1)
296438fd1498Szrj 	    {
296538fd1498Szrj 	      /* It's possible to have functions that partially overlap,
296638fd1498Szrj 		 thus take the maximum end_line of functions starting
296738fd1498Szrj 		 at LINE_NUM.  */
296838fd1498Szrj 	      for (unsigned i = 0; i < fns.size (); i++)
296938fd1498Szrj 		if (fns[i]->end_line > line_start_group)
297038fd1498Szrj 		  line_start_group = fns[i]->end_line;
297138fd1498Szrj 	    }
297238fd1498Szrj 	  else if (fns.size () == 1)
297338fd1498Szrj 	    {
297438fd1498Szrj 	      function_info *fn = fns[0];
297538fd1498Szrj 	      output_function_details (gcov_file, fn);
297638fd1498Szrj 	    }
297738fd1498Szrj 	}
297838fd1498Szrj 
297938fd1498Szrj       /* For lines which don't exist in the .bb file, print '-' before
298038fd1498Szrj 	 the source line.  For lines which exist but were never
298138fd1498Szrj 	 executed, print '#####' or '=====' before the source line.
298238fd1498Szrj 	 Otherwise, print the execution count before the source line.
298338fd1498Szrj 	 There are 16 spaces of indentation added before the source
298438fd1498Szrj 	 line so that tabs won't be messed up.  */
298538fd1498Szrj       output_line_beginning (gcov_file, line->exists, line->unexceptional,
298638fd1498Szrj 			     line->has_unexecuted_block, line->count,
298738fd1498Szrj 			     line_num, "=====", "#####");
298838fd1498Szrj 
298938fd1498Szrj       print_source_line (gcov_file, source_lines, line_num);
299038fd1498Szrj       output_line_details (gcov_file, line, line_num);
299138fd1498Szrj 
299238fd1498Szrj       if (line_start_group == line_num)
299338fd1498Szrj 	{
299438fd1498Szrj 	  for (vector<function_info *>::iterator it = fns.begin ();
299538fd1498Szrj 	       it != fns.end (); it++)
299638fd1498Szrj 	    {
299738fd1498Szrj 	      function_info *fn = *it;
299838fd1498Szrj 	      vector<line_info> &lines = fn->lines;
299938fd1498Szrj 
300038fd1498Szrj 	      fprintf (gcov_file, FN_SEPARATOR);
300138fd1498Szrj 
300238fd1498Szrj 	      string fn_name
300338fd1498Szrj 		= flag_demangled_names ? fn->demangled_name : fn->name;
300438fd1498Szrj 
300538fd1498Szrj 	      if (flag_use_colors)
300638fd1498Szrj 		{
300738fd1498Szrj 		  fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
300838fd1498Szrj 		  fn_name += SGR_RESET;
300938fd1498Szrj 		}
301038fd1498Szrj 
301138fd1498Szrj 	      fprintf (gcov_file, "%s:\n", fn_name.c_str ());
301238fd1498Szrj 
301338fd1498Szrj 	      output_function_details (gcov_file, fn);
301438fd1498Szrj 
301538fd1498Szrj 	      /* Print all lines covered by the function.  */
301638fd1498Szrj 	      for (unsigned i = 0; i < lines.size (); i++)
301738fd1498Szrj 		{
301838fd1498Szrj 		  line_info *line = &lines[i];
301938fd1498Szrj 		  unsigned l = fn->start_line + i;
302038fd1498Szrj 
302138fd1498Szrj 		  /* For lines which don't exist in the .bb file, print '-'
302238fd1498Szrj 		     before the source line.  For lines which exist but
302338fd1498Szrj 		     were never executed, print '#####' or '=====' before
302438fd1498Szrj 		     the source line.  Otherwise, print the execution count
302538fd1498Szrj 		     before the source line.
302638fd1498Szrj 		     There are 16 spaces of indentation added before the source
302738fd1498Szrj 		     line so that tabs won't be messed up.  */
302838fd1498Szrj 		  output_line_beginning (gcov_file, line->exists,
302938fd1498Szrj 					 line->unexceptional,
303038fd1498Szrj 					 line->has_unexecuted_block,
303138fd1498Szrj 					 line->count,
303238fd1498Szrj 					 l, "=====", "#####");
303338fd1498Szrj 
303438fd1498Szrj 		  print_source_line (gcov_file, source_lines, l);
303538fd1498Szrj 		  output_line_details (gcov_file, line, l);
303638fd1498Szrj 		}
303738fd1498Szrj 	    }
303838fd1498Szrj 
303938fd1498Szrj 	  fprintf (gcov_file, FN_SEPARATOR);
304038fd1498Szrj 	  line_start_group = 0;
304138fd1498Szrj 	}
304238fd1498Szrj     }
304338fd1498Szrj 
304438fd1498Szrj   if (source_file)
304538fd1498Szrj     fclose (source_file);
304638fd1498Szrj }
3047