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