1/* Gcov.c: prepend line execution counts and branch probabilities to a
2 source file.
3 Copyright (C) 1990-2024 Free Software Foundation, Inc.
4 Contributed by James E. Wilson of Cygnus Support.
5 Mangled by Bob Manson of Cygnus Support.
6 Mangled further by Nathan Sidwell <nathan@codesourcery.com>
7
8Gcov is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 3, or (at your option)
11any later version.
12
13Gcov is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with Gcov; see the file COPYING3. If not see
20<http://www.gnu.org/licenses/>. */
21
22/* ??? Print a list of the ten blocks with the highest execution counts,
23 and list the line numbers corresponding to those blocks. Also, perhaps
24 list the line numbers with the highest execution counts, only printing
25 the first if there are several which are all listed in the same block. */
26
27/* ??? Should have an option to print the number of basic blocks, and the
28 percent of them that are covered. */
29
30/* Need an option to show individual block counts, and show
31 probabilities of fall through arcs. */
32
33#include "config.h"
34#define INCLUDE_ALGORITHM
35#define INCLUDE_VECTOR
36#define INCLUDE_STRING
37#define INCLUDE_MAP
38#define INCLUDE_SET
39#include "system.h"
40#include "coretypes.h"
41#include "tm.h"
42#include "intl.h"
43#include "diagnostic.h"
44#include "version.h"
45#include "demangle.h"
46#include "color-macros.h"
47#include "pretty-print.h"
48#include "json.h"
49#include "hwint.h"
50
51#include <zlib.h>
52#include <getopt.h>
53
54#include "md5.h"
55
56using namespace std;
57
58#define IN_GCOV 1
59#include "gcov-io.h"
60#include "gcov-io.cc"
61
62#define GCOV_JSON_FORMAT_VERSION "2"
63
64/* The gcno file is generated by -ftest-coverage option. The gcda file is
65 generated by a program compiled with -fprofile-arcs. Their formats
66 are documented in gcov-io.h. */
67
68/* The functions in this file for creating and solution program flow graphs
69 are very similar to functions in the gcc source file profile.cc. In
70 some places we make use of the knowledge of how profile.cc works to
71 select particular algorithms here. */
72
73/* The code validates that the profile information read in corresponds
74 to the code currently being compiled. Rather than checking for
75 identical files, the code below compares a checksum on the CFG
76 (based on the order of basic blocks and the arcs in the CFG). If
77 the CFG checksum in the gcda file match the CFG checksum in the
78 gcno file, the profile data will be used. */
79
80/* This is the size of the buffer used to read in source file lines. */
81
82class function_info;
83class block_info;
84class source_info;
85class condition_info;
86
87/* Describes an arc between two basic blocks. */
88
89struct arc_info
90{
91 /* source and destination blocks. */
92 class block_info *src;
93 class block_info *dst;
94
95 /* transition counts. */
96 gcov_type count;
97 /* used in cycle search, so that we do not clobber original counts. */
98 gcov_type cs_count;
99
100 unsigned int count_valid : 1;
101 unsigned int on_tree : 1;
102 unsigned int fake : 1;
103 unsigned int fall_through : 1;
104
105 /* Arc to a catch handler. */
106 unsigned int is_throw : 1;
107
108 /* Arc is for a function that abnormally returns. */
109 unsigned int is_call_non_return : 1;
110
111 /* Arc is for catch/setjmp. */
112 unsigned int is_nonlocal_return : 1;
113
114 /* Is an unconditional branch. */
115 unsigned int is_unconditional : 1;
116
117 /* Loop making arc. */
118 unsigned int cycle : 1;
119
120 /* Links to next arc on src and dst lists. */
121 struct arc_info *succ_next;
122 struct arc_info *pred_next;
123};
124
125/* Describes which locations (lines and files) are associated with
126 a basic block. */
127
128class block_location_info
129{
130public:
131 block_location_info (unsigned _source_file_idx):
132 source_file_idx (_source_file_idx)
133 {}
134
135 unsigned source_file_idx;
136 vector<unsigned> lines;
137};
138
139/* Describes a single conditional expression and the (recorded) conditions
140 shown to independently affect the outcome. */
141class condition_info
142{
143public:
144 condition_info ();
145
146 int popcount () const;
147
148 /* Bitsets storing the independently significant outcomes for true and false,
149 respectively. */
150 gcov_type_unsigned truev;
151 gcov_type_unsigned falsev;
152
153 /* Number of terms in the expression; if (x) -> 1, if (x && y) -> 2 etc. */
154 unsigned n_terms;
155};
156
157condition_info::condition_info (): truev (0), falsev (0), n_terms (0)
158{
159}
160
161int condition_info::popcount () const
162{
163 return popcount_hwi (x: truev) + popcount_hwi (x: falsev);
164}
165
166/* Describes a basic block. Contains lists of arcs to successor and
167 predecessor blocks. */
168
169class block_info
170{
171public:
172 /* Constructor. */
173 block_info ();
174
175 /* Chain of exit and entry arcs. */
176 arc_info *succ;
177 arc_info *pred;
178
179 /* Number of unprocessed exit and entry arcs. */
180 gcov_type num_succ;
181 gcov_type num_pred;
182
183 unsigned id;
184
185 /* Block execution count. */
186 gcov_type count;
187 unsigned count_valid : 1;
188 unsigned valid_chain : 1;
189 unsigned invalid_chain : 1;
190 unsigned exceptional : 1;
191
192 /* Block is a call instrumenting site. */
193 unsigned is_call_site : 1; /* Does the call. */
194 unsigned is_call_return : 1; /* Is the return. */
195
196 /* Block is a landing pad for longjmp or throw. */
197 unsigned is_nonlocal_return : 1;
198
199 condition_info conditions;
200
201 vector<block_location_info> locations;
202
203 struct
204 {
205 /* Single line graph cycle workspace. Used for all-blocks
206 mode. */
207 arc_info *arc;
208 unsigned ident;
209 } cycle; /* Used in all-blocks mode, after blocks are linked onto
210 lines. */
211
212 /* Temporary chain for solving graph, and for chaining blocks on one
213 line. */
214 class block_info *chain;
215
216};
217
218block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
219 id (0), count (0), count_valid (0), valid_chain (0), invalid_chain (0),
220 exceptional (0), is_call_site (0), is_call_return (0), is_nonlocal_return (0),
221 locations (), chain (NULL)
222{
223 cycle.arc = NULL;
224}
225
226/* Describes a single line of source. Contains a chain of basic blocks
227 with code on it. */
228
229class line_info
230{
231public:
232 /* Default constructor. */
233 line_info ();
234
235 /* Return true when NEEDLE is one of basic blocks the line belongs to. */
236 bool has_block (block_info *needle);
237
238 /* Execution count. */
239 gcov_type count;
240
241 /* Branches from blocks that end on this line. */
242 vector<arc_info *> branches;
243
244 /* blocks which start on this line. Used in all-blocks mode. */
245 vector<block_info *> blocks;
246
247 unsigned exists : 1;
248 unsigned unexceptional : 1;
249 unsigned has_unexecuted_block : 1;
250};
251
252line_info::line_info (): count (0), branches (), blocks (), exists (false),
253 unexceptional (0), has_unexecuted_block (0)
254{
255}
256
257bool
258line_info::has_block (block_info *needle)
259{
260 return std::find (first: blocks.begin (), last: blocks.end (), val: needle) != blocks.end ();
261}
262
263/* Output demangled function names. */
264
265static int flag_demangled_names = 0;
266
267/* Describes a single function. Contains an array of basic blocks. */
268
269class function_info
270{
271public:
272 function_info ();
273 ~function_info ();
274
275 /* Return true when line N belongs to the function in source file SRC_IDX.
276 The line must be defined in body of the function, can't be inlined. */
277 bool group_line_p (unsigned n, unsigned src_idx);
278
279 /* Function filter based on function_info::artificial variable. */
280
281 static inline bool
282 is_artificial (function_info *fn)
283 {
284 return fn->artificial;
285 }
286
287 /* Name of function. */
288 char *m_name;
289 char *m_demangled_name;
290 unsigned ident;
291 unsigned lineno_checksum;
292 unsigned cfg_checksum;
293
294 /* The graph contains at least one fake incoming edge. */
295 unsigned has_catch : 1;
296
297 /* True when the function is artificial and does not exist
298 in a source file. */
299 unsigned artificial : 1;
300
301 /* True when multiple functions start at a line in a source file. */
302 unsigned is_group : 1;
303
304 /* Array of basic blocks. Like in GCC, the entry block is
305 at blocks[0] and the exit block is at blocks[1]. */
306#define ENTRY_BLOCK (0)
307#define EXIT_BLOCK (1)
308 vector<block_info> blocks;
309 unsigned blocks_executed;
310
311 vector<condition_info*> conditions;
312
313 /* Raw arc coverage counts. */
314 vector<gcov_type> counts;
315
316 /* First line number. */
317 unsigned start_line;
318
319 /* First line column. */
320 unsigned start_column;
321
322 /* Last line number. */
323 unsigned end_line;
324
325 /* Last line column. */
326 unsigned end_column;
327
328 /* Index of source file where the function is defined. */
329 unsigned src;
330
331 /* Vector of line information (used only for group functions). */
332 vector<line_info> lines;
333
334 /* Next function. */
335 class function_info *next;
336
337 /* Get demangled name of a function. The demangled name
338 is converted when it is used for the first time. */
339 char *get_demangled_name ()
340 {
341 if (m_demangled_name == NULL)
342 {
343 m_demangled_name = cplus_demangle (mangled: m_name, DMGL_PARAMS);
344 if (!m_demangled_name)
345 m_demangled_name = m_name;
346 }
347
348 return m_demangled_name;
349 }
350
351 /* Get name of the function based on flag_demangled_names. */
352 char *get_name ()
353 {
354 return flag_demangled_names ? get_demangled_name () : m_name;
355 }
356
357 /* Return number of basic blocks (without entry and exit block). */
358 unsigned get_block_count ()
359 {
360 return blocks.size () - 2;
361 }
362};
363
364/* Function info comparer that will sort functions according to starting
365 line. */
366
367struct function_line_start_cmp
368{
369 inline bool operator() (const function_info *lhs,
370 const function_info *rhs)
371 {
372 return (lhs->start_line == rhs->start_line
373 ? lhs->start_column < rhs->start_column
374 : lhs->start_line < rhs->start_line);
375 }
376};
377
378/* Describes coverage of a file or function. */
379
380struct coverage_info
381{
382 int lines;
383 int lines_executed;
384
385 int branches;
386 int branches_executed;
387 int branches_taken;
388
389 int conditions;
390 int conditions_covered;
391
392 int calls;
393 int calls_executed;
394
395 char *name;
396};
397
398/* Describes a file mentioned in the block graph. Contains an array
399 of line info. */
400
401class source_info
402{
403public:
404 /* Default constructor. */
405 source_info ();
406
407 vector<function_info *> *get_functions_at_location (unsigned line_num) const;
408
409 /* Register a new function. */
410 void add_function (function_info *fn);
411
412 /* Debug the source file. */
413 void debug ();
414
415 /* Index of the source_info in sources vector. */
416 unsigned index;
417
418 /* Canonical name of source file. */
419 char *name;
420 time_t file_time;
421
422 /* Vector of line information. */
423 vector<line_info> lines;
424
425 coverage_info coverage;
426
427 /* Maximum line count in the source file. */
428 unsigned int maximum_count;
429
430 /* Functions in this source file. These are in ascending line
431 number order. */
432 vector<function_info *> functions;
433
434 /* Line number to functions map. */
435 vector<vector<function_info *> *> line_to_function_map;
436};
437
438source_info::source_info (): index (0), name (NULL), file_time (),
439 lines (), coverage (), maximum_count (0), functions ()
440{
441}
442
443/* Register a new function. */
444void
445source_info::add_function (function_info *fn)
446{
447 functions.push_back (x: fn);
448
449 if (fn->start_line >= line_to_function_map.size ())
450 line_to_function_map.resize (new_size: fn->start_line + 1);
451
452 vector<function_info *> **slot = &line_to_function_map[fn->start_line];
453 if (*slot == NULL)
454 *slot = new vector<function_info *> ();
455
456 (*slot)->push_back (x: fn);
457}
458
459vector<function_info *> *
460source_info::get_functions_at_location (unsigned line_num) const
461{
462 if (line_num >= line_to_function_map.size ())
463 return NULL;
464
465 vector<function_info *> *slot = line_to_function_map[line_num];
466 if (slot != NULL)
467 std::sort (first: slot->begin (), last: slot->end (), comp: function_line_start_cmp ());
468
469 return slot;
470}
471
472void source_info::debug ()
473{
474 fprintf (stderr, format: "source_info: %s\n", name);
475 for (vector<function_info *>::iterator it = functions.begin ();
476 it != functions.end (); it++)
477 {
478 function_info *fn = *it;
479 fprintf (stderr, format: " function_info: %s\n", fn->get_name ());
480 for (vector<block_info>::iterator bit = fn->blocks.begin ();
481 bit != fn->blocks.end (); bit++)
482 {
483 fprintf (stderr, format: " block_info id=%d, count=%" PRId64 " \n",
484 bit->id, bit->count);
485 }
486 }
487
488 for (unsigned lineno = 1; lineno < lines.size (); ++lineno)
489 {
490 line_info &line = lines[lineno];
491 fprintf (stderr, format: " line_info=%d, count=%" PRId64 "\n", lineno, line.count);
492 }
493
494 fprintf (stderr, format: "\n");
495}
496
497class name_map
498{
499public:
500 name_map ()
501 {
502 }
503
504 name_map (char *_name, unsigned _src): name (_name), src (_src)
505 {
506 }
507
508 bool operator== (const name_map &rhs) const
509 {
510#if HAVE_DOS_BASED_FILE_SYSTEM
511 return strcasecmp (this->name, rhs.name) == 0;
512#else
513 return strcmp (s1: this->name, s2: rhs.name) == 0;
514#endif
515 }
516
517 bool operator< (const name_map &rhs) const
518 {
519#if HAVE_DOS_BASED_FILE_SYSTEM
520 return strcasecmp (this->name, rhs.name) < 0;
521#else
522 return strcmp (s1: this->name, s2: rhs.name) < 0;
523#endif
524 }
525
526 const char *name; /* Source file name */
527 unsigned src; /* Source file */
528};
529
530/* Vector of all functions. */
531static vector<function_info *> functions;
532
533/* Function ident to function_info * map. */
534static map<unsigned, function_info *> ident_to_fn;
535
536/* Vector of source files. */
537static vector<source_info> sources;
538
539/* Mapping of file names to sources */
540static vector<name_map> names;
541
542/* Record all processed files in order to warn about
543 a file being read multiple times. */
544static vector<char *> processed_files;
545
546/* This holds data summary information. */
547
548static unsigned object_runs;
549
550static unsigned total_lines;
551static unsigned total_executed;
552
553/* Modification time of graph file. */
554
555static time_t bbg_file_time;
556
557/* Name of the notes (gcno) output file. The "bbg" prefix is for
558 historical reasons, when the notes file contained only the
559 basic block graph notes. */
560
561static char *bbg_file_name;
562
563/* Stamp of the bbg file */
564static unsigned bbg_stamp;
565
566/* Supports has_unexecuted_blocks functionality. */
567static unsigned bbg_supports_has_unexecuted_blocks;
568
569/* Working directory in which a TU was compiled. */
570static const char *bbg_cwd;
571
572/* Name and file pointer of the input file for the count data (gcda). */
573
574static char *da_file_name;
575
576/* Data file is missing. */
577
578static int no_data_file;
579
580/* If there is several input files, compute and display results after
581 reading all data files. This way if two or more gcda file refer to
582 the same source file (eg inline subprograms in a .h file), the
583 counts are added. */
584
585static int multiple_files = 0;
586
587/* Output branch probabilities. */
588
589static int flag_branches = 0;
590
591/* Output conditions (modified condition/decision coverage). */
592
593static bool flag_conditions = 0;
594
595/* Show unconditional branches too. */
596static int flag_unconditional = 0;
597
598/* Output a gcov file if this is true. This is on by default, and can
599 be turned off by the -n option. */
600
601static int flag_gcov_file = 1;
602
603/* Output to stdout instead to a gcov file. */
604
605static int flag_use_stdout = 0;
606
607/* Output progress indication if this is true. This is off by default
608 and can be turned on by the -d option. */
609
610static int flag_display_progress = 0;
611
612/* Output *.gcov file in JSON intermediate format used by consumers. */
613
614static int flag_json_format = 0;
615
616/* For included files, make the gcov output file name include the name
617 of the input source file. For example, if x.h is included in a.c,
618 then the output file name is a.c##x.h.gcov instead of x.h.gcov. */
619
620static int flag_long_names = 0;
621
622/* For situations when a long name can potentially hit filesystem path limit,
623 let's calculate md5sum of the path and append it to a file name. */
624
625static int flag_hash_filenames = 0;
626
627/* Print verbose informations. */
628
629static int flag_verbose = 0;
630
631/* Print colored output. */
632
633static int flag_use_colors = 0;
634
635/* Use perf-like colors to indicate hot lines. */
636
637static int flag_use_hotness_colors = 0;
638
639/* Output count information for every basic block, not merely those
640 that contain line number information. */
641
642static int flag_all_blocks = 0;
643
644/* Output human readable numbers. */
645
646static int flag_human_readable_numbers = 0;
647
648/* Output summary info for each function. */
649
650static int flag_function_summary = 0;
651
652/* Print debugging dumps. */
653
654static int flag_debug = 0;
655
656/* Object directory file prefix. This is the directory/file where the
657 graph and data files are looked for, if nonzero. */
658
659static char *object_directory = 0;
660
661/* Source directory prefix. This is removed from source pathnames
662 that match, when generating the output file name. */
663
664static char *source_prefix = 0;
665static size_t source_length = 0;
666
667/* Only show data for sources with relative pathnames. Absolute ones
668 usually indicate a system header file, which although it may
669 contain inline functions, is usually uninteresting. */
670static int flag_relative_only = 0;
671
672/* Preserve all pathname components. Needed when object files and
673 source files are in subdirectories. '/' is mangled as '#', '.' is
674 elided and '..' mangled to '^'. */
675
676static int flag_preserve_paths = 0;
677
678/* Output the number of times a branch was taken as opposed to the percentage
679 of times it was taken. */
680
681static int flag_counts = 0;
682
683/* Return code of the tool invocation. */
684static int return_code = 0;
685
686/* Forward declarations. */
687static int process_args (int, char **);
688static void print_usage (int) ATTRIBUTE_NORETURN;
689static void print_version (void) ATTRIBUTE_NORETURN;
690static void process_file (const char *);
691static void process_all_functions (void);
692static void generate_results (const char *);
693static void create_file_names (const char *);
694static char *canonicalize_name (const char *);
695static unsigned find_source (const char *);
696static void read_graph_file (void);
697static int read_count_file (void);
698static void solve_flow_graph (function_info *);
699static void find_exception_blocks (function_info *);
700static void add_branch_counts (coverage_info *, const arc_info *);
701static void add_condition_counts (coverage_info *, const block_info *);
702static void add_line_counts (coverage_info *, function_info *);
703static void executed_summary (unsigned, unsigned);
704static void function_summary (const coverage_info *);
705static void file_summary (const coverage_info *);
706static const char *format_gcov (gcov_type, gcov_type, int);
707static void accumulate_line_counts (source_info *);
708static void output_gcov_file (const char *, source_info *);
709static int output_branch_count (FILE *, int, const arc_info *);
710static void output_conditions (FILE *, const block_info *);
711static void output_lines (FILE *, const source_info *);
712static string make_gcov_file_name (const char *, const char *);
713static char *mangle_name (const char *);
714static void release_structures (void);
715extern int main (int, char **);
716
717function_info::function_info (): m_name (NULL), m_demangled_name (NULL),
718 ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
719 artificial (0), is_group (0),
720 blocks (), blocks_executed (0), counts (),
721 start_line (0), start_column (0), end_line (0), end_column (0),
722 src (0), lines (), next (NULL)
723{
724}
725
726function_info::~function_info ()
727{
728 for (int i = blocks.size () - 1; i >= 0; i--)
729 {
730 arc_info *arc, *arc_n;
731
732 for (arc = blocks[i].succ; arc; arc = arc_n)
733 {
734 arc_n = arc->succ_next;
735 free (ptr: arc);
736 }
737 }
738 if (m_demangled_name != m_name)
739 free (ptr: m_demangled_name);
740 free (ptr: m_name);
741}
742
743bool function_info::group_line_p (unsigned n, unsigned src_idx)
744{
745 return is_group && src == src_idx && start_line <= n && n <= end_line;
746}
747
748/* Cycle detection!
749 There are a bajillion algorithms that do this. Boost's function is named
750 hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
751 "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs"
752 (url at <http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf>).
753
754 The basic algorithm is simple: effectively, we're finding all simple paths
755 in a subgraph (that shrinks every iteration). Duplicates are filtered by
756 "blocking" a path when a node is added to the path (this also prevents non-
757 simple paths)--the node is unblocked only when it participates in a cycle.
758 */
759
760typedef vector<arc_info *> arc_vector_t;
761typedef vector<const block_info *> block_vector_t;
762
763/* Handle cycle identified by EDGES, where the function finds minimum cs_count
764 and subtract the value from all counts. The subtracted value is added
765 to COUNT. Returns type of loop. */
766
767static void
768handle_cycle (const arc_vector_t &edges, int64_t &count)
769{
770 /* Find the minimum edge of the cycle, and reduce all nodes in the cycle by
771 that amount. */
772 int64_t cycle_count = INTTYPE_MAXIMUM (int64_t);
773 for (unsigned i = 0; i < edges.size (); i++)
774 {
775 int64_t ecount = edges[i]->cs_count;
776 if (cycle_count > ecount)
777 cycle_count = ecount;
778 }
779 count += cycle_count;
780 for (unsigned i = 0; i < edges.size (); i++)
781 edges[i]->cs_count -= cycle_count;
782
783 gcc_assert (cycle_count > 0);
784}
785
786/* Unblock a block U from BLOCKED. Apart from that, iterate all blocks
787 blocked by U in BLOCK_LISTS. */
788
789static void
790unblock (const block_info *u, block_vector_t &blocked,
791 vector<block_vector_t > &block_lists)
792{
793 block_vector_t::iterator it = find (first: blocked.begin (), last: blocked.end (), val: u);
794 if (it == blocked.end ())
795 return;
796
797 unsigned index = it - blocked.begin ();
798 blocked.erase (position: it);
799
800 block_vector_t to_unblock (block_lists[index]);
801
802 block_lists.erase (position: block_lists.begin () + index);
803
804 for (block_vector_t::iterator it = to_unblock.begin ();
805 it != to_unblock.end (); it++)
806 unblock (u: *it, blocked, block_lists);
807}
808
809/* Return true when PATH contains a zero cycle arc count. */
810
811static bool
812path_contains_zero_or_negative_cycle_arc (arc_vector_t &path)
813{
814 for (unsigned i = 0; i < path.size (); i++)
815 if (path[i]->cs_count <= 0)
816 return true;
817 return false;
818}
819
820/* Find circuit going to block V, PATH is provisional seen cycle.
821 BLOCKED is vector of blocked vertices, BLOCK_LISTS contains vertices
822 blocked by a block. COUNT is accumulated count of the current LINE.
823 Returns what type of loop it contains. */
824
825static bool
826circuit (block_info *v, arc_vector_t &path, block_info *start,
827 block_vector_t &blocked, vector<block_vector_t> &block_lists,
828 line_info &linfo, int64_t &count)
829{
830 bool loop_found = false;
831
832 /* Add v to the block list. */
833 gcc_assert (find (blocked.begin (), blocked.end (), v) == blocked.end ());
834 blocked.push_back (x: v);
835 block_lists.push_back (x: block_vector_t ());
836
837 for (arc_info *arc = v->succ; arc; arc = arc->succ_next)
838 {
839 block_info *w = arc->dst;
840 if (w < start
841 || arc->cs_count <= 0
842 || !linfo.has_block (needle: w))
843 continue;
844
845 path.push_back (x: arc);
846 if (w == start)
847 {
848 /* Cycle has been found. */
849 handle_cycle (edges: path, count);
850 loop_found = true;
851 }
852 else if (!path_contains_zero_or_negative_cycle_arc (path)
853 && find (first: blocked.begin (), last: blocked.end (), val: w) == blocked.end ())
854 loop_found |= circuit (v: w, path, start, blocked, block_lists, linfo,
855 count);
856
857 path.pop_back ();
858 }
859
860 if (loop_found)
861 unblock (u: v, blocked, block_lists);
862 else
863 for (arc_info *arc = v->succ; arc; arc = arc->succ_next)
864 {
865 block_info *w = arc->dst;
866 if (w < start
867 || arc->cs_count <= 0
868 || !linfo.has_block (needle: w))
869 continue;
870
871 size_t index
872 = find (first: blocked.begin (), last: blocked.end (), val: w) - blocked.begin ();
873 gcc_assert (index < blocked.size ());
874 block_vector_t &list = block_lists[index];
875 if (find (first: list.begin (), last: list.end (), val: v) == list.end ())
876 list.push_back (x: v);
877 }
878
879 return loop_found;
880}
881
882/* Find cycles for a LINFO. */
883
884static gcov_type
885get_cycles_count (line_info &linfo)
886{
887 /* Note that this algorithm works even if blocks aren't in sorted order.
888 Each iteration of the circuit detection is completely independent
889 (except for reducing counts, but that shouldn't matter anyways).
890 Therefore, operating on a permuted order (i.e., non-sorted) only
891 has the effect of permuting the output cycles. */
892
893 gcov_type count = 0;
894 for (vector<block_info *>::iterator it = linfo.blocks.begin ();
895 it != linfo.blocks.end (); it++)
896 {
897 arc_vector_t path;
898 block_vector_t blocked;
899 vector<block_vector_t > block_lists;
900 circuit (v: *it, path, start: *it, blocked, block_lists, linfo, count);
901 }
902
903 return count;
904}
905
906int
907main (int argc, char **argv)
908{
909 int argno;
910 int first_arg;
911 const char *p;
912
913 p = argv[0] + strlen (s: argv[0]);
914 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
915 --p;
916 progname = p;
917
918 xmalloc_set_program_name (progname);
919
920 /* Unlock the stdio streams. */
921 unlock_std_streams ();
922
923 gcc_init_libintl ();
924
925 diagnostic_initialize (context: global_dc, n_opts: 0);
926
927 /* Handle response files. */
928 expandargv (&argc, &argv);
929
930 argno = process_args (argc, argv);
931 if (optind == argc)
932 print_usage (true);
933
934 if (argc - argno > 1)
935 multiple_files = 1;
936
937 first_arg = argno;
938
939 for (; argno != argc; argno++)
940 {
941 if (flag_display_progress)
942 printf (format: "Processing file %d out of %d\n", argno - first_arg + 1,
943 argc - first_arg);
944 process_file (argv[argno]);
945
946 if (flag_json_format || argno == argc - 1)
947 {
948 process_all_functions ();
949 generate_results (argv[argno]);
950 release_structures ();
951 }
952 }
953
954 if (!flag_use_stdout)
955 executed_summary (total_lines, total_executed);
956
957 return return_code;
958}
959
960/* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
961 otherwise the output of --help. */
962
963static void
964print_usage (int error_p)
965{
966 FILE *file = error_p ? stderr : stdout;
967 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
968
969 fnotice (file, "Usage: gcov [OPTION...] SOURCE|OBJ...\n\n");
970 fnotice (file, "Print code coverage information.\n\n");
971 fnotice (file, " -a, --all-blocks Show information for every basic block\n");
972 fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n");
973 fnotice (file, " -c, --branch-counts Output counts of branches taken\n\
974 rather than percentages\n");
975 fnotice (file, " -g, --conditions Include modified condition/decision\n\
976 coverage in output\n");
977 fnotice (file, " -d, --display-progress Display progress information\n");
978 fnotice (file, " -D, --debug Display debugging dumps\n");
979 fnotice (file, " -f, --function-summaries Output summaries for each function\n");
980 fnotice (file, " -h, --help Print this help, then exit\n");
981 fnotice (file, " -j, --json-format Output JSON intermediate format\n\
982 into .gcov.json.gz file\n");
983 fnotice (file, " -H, --human-readable Output human readable numbers\n");
984 fnotice (file, " -k, --use-colors Emit colored output\n");
985 fnotice (file, " -l, --long-file-names Use long output file names for included\n\
986 source files\n");
987 fnotice (file, " -m, --demangled-names Output demangled function names\n");
988 fnotice (file, " -n, --no-output Do not create an output file\n");
989 fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
990 fnotice (file, " -p, --preserve-paths Preserve all pathname components\n");
991 fnotice (file, " -q, --use-hotness-colors Emit perf-like colored output for hot lines\n");
992 fnotice (file, " -r, --relative-only Only show data for relative sources\n");
993 fnotice (file, " -s, --source-prefix DIR Source prefix to elide\n");
994 fnotice (file, " -t, --stdout Output to stdout instead of a file\n");
995 fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n");
996 fnotice (file, " -v, --version Print version number, then exit\n");
997 fnotice (file, " -w, --verbose Print verbose informations\n");
998 fnotice (file, " -x, --hash-filenames Hash long pathnames\n");
999 fnotice (file, "\nObsolete options:\n");
1000 fnotice (file, " -i, --json-format Replaced with -j, --json-format\n");
1001 fnotice (file, " -j, --human-readable Replaced with -H, --human-readable\n");
1002 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
1003 bug_report_url);
1004 exit (status: status);
1005}
1006
1007/* Print version information and exit. */
1008
1009static void
1010print_version (void)
1011{
1012 fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
1013 fnotice (stdout, "JSON format version: %s\n", GCOV_JSON_FORMAT_VERSION);
1014 fprintf (stdout, format: "Copyright %s 2024 Free Software Foundation, Inc.\n",
1015 _("(C)"));
1016 fnotice (stdout,
1017 _("This is free software; see the source for copying conditions. There is NO\n\
1018warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
1019 exit (SUCCESS_EXIT_CODE);
1020}
1021
1022static const struct option options[] =
1023{
1024 { .name: "help", no_argument, NULL, .val: 'h' },
1025 { .name: "version", no_argument, NULL, .val: 'v' },
1026 { .name: "verbose", no_argument, NULL, .val: 'w' },
1027 { .name: "all-blocks", no_argument, NULL, .val: 'a' },
1028 { .name: "branch-probabilities", no_argument, NULL, .val: 'b' },
1029 { .name: "branch-counts", no_argument, NULL, .val: 'c' },
1030 { .name: "conditions", no_argument, NULL, .val: 'g' },
1031 { .name: "json-format", no_argument, NULL, .val: 'j' },
1032 { .name: "human-readable", no_argument, NULL, .val: 'H' },
1033 { .name: "no-output", no_argument, NULL, .val: 'n' },
1034 { .name: "long-file-names", no_argument, NULL, .val: 'l' },
1035 { .name: "function-summaries", no_argument, NULL, .val: 'f' },
1036 { .name: "demangled-names", no_argument, NULL, .val: 'm' },
1037 { .name: "preserve-paths", no_argument, NULL, .val: 'p' },
1038 { .name: "relative-only", no_argument, NULL, .val: 'r' },
1039 { .name: "object-directory", required_argument, NULL, .val: 'o' },
1040 { .name: "object-file", required_argument, NULL, .val: 'o' },
1041 { .name: "source-prefix", required_argument, NULL, .val: 's' },
1042 { .name: "stdout", no_argument, NULL, .val: 't' },
1043 { .name: "unconditional-branches", no_argument, NULL, .val: 'u' },
1044 { .name: "display-progress", no_argument, NULL, .val: 'd' },
1045 { .name: "hash-filenames", no_argument, NULL, .val: 'x' },
1046 { .name: "use-colors", no_argument, NULL, .val: 'k' },
1047 { .name: "use-hotness-colors", no_argument, NULL, .val: 'q' },
1048 { .name: "debug", no_argument, NULL, .val: 'D' },
1049 { .name: 0, .has_arg: 0, .flag: 0, .val: 0 }
1050};
1051
1052/* Process args, return index to first non-arg. */
1053
1054static int
1055process_args (int argc, char **argv)
1056{
1057 int opt;
1058
1059 const char *opts = "abcdDfghHijklmno:pqrs:tuvwx";
1060 while ((opt = getopt_long (argc, argv, shortopts: opts, longopts: options, NULL)) != -1)
1061 {
1062 switch (opt)
1063 {
1064 case 'a':
1065 flag_all_blocks = 1;
1066 break;
1067 case 'b':
1068 flag_branches = 1;
1069 break;
1070 case 'c':
1071 flag_counts = 1;
1072 break;
1073 case 'f':
1074 flag_function_summary = 1;
1075 break;
1076 case 'g':
1077 flag_conditions = 1;
1078 break;
1079 case 'h':
1080 print_usage (error_p: false);
1081 /* print_usage will exit. */
1082 case 'l':
1083 flag_long_names = 1;
1084 break;
1085 case 'H':
1086 flag_human_readable_numbers = 1;
1087 break;
1088 case 'k':
1089 flag_use_colors = 1;
1090 break;
1091 case 'q':
1092 flag_use_hotness_colors = 1;
1093 break;
1094 case 'm':
1095 flag_demangled_names = 1;
1096 break;
1097 case 'n':
1098 flag_gcov_file = 0;
1099 break;
1100 case 'o':
1101 object_directory = optarg;
1102 break;
1103 case 's':
1104 source_prefix = optarg;
1105 source_length = strlen (s: source_prefix);
1106 break;
1107 case 'r':
1108 flag_relative_only = 1;
1109 break;
1110 case 'p':
1111 flag_preserve_paths = 1;
1112 break;
1113 case 'u':
1114 flag_unconditional = 1;
1115 break;
1116 case 'i':
1117 case 'j':
1118 flag_json_format = 1;
1119 flag_gcov_file = 1;
1120 break;
1121 case 'd':
1122 flag_display_progress = 1;
1123 break;
1124 case 'x':
1125 flag_hash_filenames = 1;
1126 break;
1127 case 'w':
1128 flag_verbose = 1;
1129 break;
1130 case 't':
1131 flag_use_stdout = 1;
1132 break;
1133 case 'D':
1134 flag_debug = 1;
1135 break;
1136 case 'v':
1137 print_version ();
1138 /* print_version will exit. */
1139 default:
1140 print_usage (error_p: true);
1141 /* print_usage will exit. */
1142 }
1143 }
1144
1145 return optind;
1146}
1147
1148/* Output intermediate LINE sitting on LINE_NUM to JSON OBJECT.
1149 Add FUNCTION_NAME to the LINE. */
1150
1151static void
1152output_intermediate_json_line (json::array *object,
1153 line_info *line, unsigned line_num,
1154 const char *function_name)
1155{
1156 if (!line->exists)
1157 return;
1158
1159 json::object *lineo = new json::object ();
1160 lineo->set_integer (key: "line_number", v: line_num);
1161 if (function_name != NULL)
1162 lineo->set_string (key: "function_name", utf8_value: function_name);
1163 lineo->set_integer (key: "count", v: line->count);
1164 lineo->set_bool (key: "unexecuted_block", v: line->has_unexecuted_block);
1165
1166 json::array *bb_ids = new json::array ();
1167 for (const block_info *block : line->blocks)
1168 bb_ids->append (v: new json::integer_number (block->id));
1169 lineo->set (key: "block_ids", v: bb_ids);
1170
1171 json::array *branches = new json::array ();
1172 lineo->set (key: "branches", v: branches);
1173
1174 json::array *calls = new json::array ();
1175 lineo->set (key: "calls", v: calls);
1176
1177 vector<arc_info *>::const_iterator it;
1178 if (flag_branches)
1179 for (it = line->branches.begin (); it != line->branches.end ();
1180 it++)
1181 {
1182 if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
1183 {
1184 json::object *branch = new json::object ();
1185 branch->set_integer (key: "count", v: (*it)->count);
1186 branch->set_bool (key: "throw", v: (*it)->is_throw);
1187 branch->set_bool (key: "fallthrough", v: (*it)->fall_through);
1188 branch->set_integer (key: "source_block_id", v: (*it)->src->id);
1189 branch->set_integer (key: "destination_block_id", v: (*it)->dst->id);
1190 branches->append (v: branch);
1191 }
1192 else if ((*it)->is_call_non_return)
1193 {
1194 json::object *call = new json::object ();
1195 gcov_type returns = (*it)->src->count - (*it)->count;
1196 call->set_integer (key: "source_block_id", v: (*it)->src->id);
1197 call->set_integer (key: "destination_block_id", v: (*it)->dst->id);
1198 call->set_integer (key: "returned", v: returns);
1199 calls->append (v: call);
1200 }
1201 }
1202
1203 json::array *conditions = new json::array ();
1204 lineo->set (key: "conditions", v: conditions);
1205 if (flag_conditions)
1206 {
1207 vector<block_info *>::const_iterator it;
1208 for (it = line->blocks.begin (); it != line->blocks.end (); it++)
1209 {
1210 const condition_info& info = (*it)->conditions;
1211 if (info.n_terms == 0)
1212 continue;
1213
1214 const int count = 2 * info.n_terms;
1215 const int covered = info.popcount ();
1216
1217 json::object *cond = new json::object ();
1218 cond->set (key: "count", v: new json::integer_number (count));
1219 cond->set (key: "covered", v: new json::integer_number (covered));
1220
1221 json::array *mtrue = new json::array ();
1222 json::array *mfalse = new json::array ();
1223 cond->set (key: "not_covered_true", v: mtrue);
1224 cond->set (key: "not_covered_false", v: mfalse);
1225
1226 if (count != covered)
1227 {
1228 for (unsigned i = 0; i < info.n_terms; i++)
1229 {
1230 gcov_type_unsigned index = 1;
1231 index <<= i;
1232 if (!(index & info.truev))
1233 mtrue->append (v: new json::integer_number (i));
1234 if (!(index & info.falsev))
1235 mfalse->append (v: new json::integer_number (i));
1236 }
1237 }
1238 conditions->append (v: cond);
1239 }
1240 }
1241
1242 object->append (v: lineo);
1243}
1244
1245/* Strip filename extension in STR. */
1246
1247static string
1248strip_extention (string str)
1249{
1250 string::size_type pos = str.rfind (c: '.');
1251 if (pos != string::npos)
1252 str = str.substr (pos: 0, n: pos);
1253
1254 return str;
1255}
1256
1257/* Calcualte md5sum for INPUT string and return it in hex string format. */
1258
1259static string
1260get_md5sum (const char *input)
1261{
1262 md5_ctx ctx;
1263 char md5sum[16];
1264 string str;
1265
1266 md5_init_ctx (ctx: &ctx);
1267 md5_process_bytes (buffer: input, len: strlen (s: input), ctx: &ctx);
1268 md5_finish_ctx (ctx: &ctx, resbuf: md5sum);
1269
1270 for (unsigned i = 0; i < 16; i++)
1271 {
1272 char b[3];
1273 sprintf (s: b, format: "%02x", (unsigned char)md5sum[i]);
1274 str += b;
1275 }
1276
1277 return str;
1278}
1279
1280/* Get the name of the gcov file. The return value must be free'd.
1281
1282 It appends the '.gcov' extension to the *basename* of the file.
1283 The resulting file name will be in PWD.
1284
1285 e.g.,
1286 input: foo.da, output: foo.da.gcov
1287 input: a/b/foo.cc, output: foo.cc.gcov */
1288
1289static string
1290get_gcov_intermediate_filename (const char *input_file_name)
1291{
1292 string base = basename (filename: input_file_name);
1293 string str = strip_extention (str: base);
1294
1295 if (flag_hash_filenames)
1296 {
1297 str += "##";
1298 str += get_md5sum (input: input_file_name);
1299 }
1300 else if (flag_preserve_paths && base != input_file_name)
1301 {
1302 str += "##";
1303 str += mangle_path (base: input_file_name);
1304 str = strip_extention (str);
1305 }
1306
1307 str += ".gcov.json.gz";
1308 return str.c_str ();
1309}
1310
1311/* Output the result in JSON intermediate format.
1312 Source info SRC is dumped into JSON_FILES which is JSON array. */
1313
1314static void
1315output_json_intermediate_file (json::array *json_files, source_info *src)
1316{
1317 json::object *root = new json::object ();
1318 json_files->append (v: root);
1319
1320 root->set_string (key: "file", utf8_value: src->name);
1321
1322 json::array *functions = new json::array ();
1323 root->set (key: "functions", v: functions);
1324
1325 std::sort (first: src->functions.begin (), last: src->functions.end (),
1326 comp: function_line_start_cmp ());
1327 for (vector<function_info *>::iterator it = src->functions.begin ();
1328 it != src->functions.end (); it++)
1329 {
1330 json::object *function = new json::object ();
1331 function->set_string (key: "name", utf8_value: (*it)->m_name);
1332 function->set_string (key: "demangled_name", utf8_value: (*it)->get_demangled_name ());
1333 function->set_integer (key: "start_line", v: (*it)->start_line);
1334 function->set_integer (key: "start_column", v: (*it)->start_column);
1335 function->set_integer (key: "end_line", v: (*it)->end_line);
1336 function->set_integer (key: "end_column", v: (*it)->end_column);
1337 function->set_integer (key: "blocks", v: (*it)->get_block_count ());
1338 function->set_integer (key: "blocks_executed", v: (*it)->blocks_executed);
1339 function->set_integer (key: "execution_count", v: (*it)->blocks[0].count);
1340
1341 functions->append (v: function);
1342 }
1343
1344 json::array *lineso = new json::array ();
1345 root->set (key: "lines", v: lineso);
1346
1347 vector<function_info *> last_non_group_fns;
1348
1349 for (unsigned line_num = 1; line_num <= src->lines.size (); line_num++)
1350 {
1351 vector<function_info *> *fns = src->get_functions_at_location (line_num);
1352
1353 if (fns != NULL)
1354 /* Print info for all group functions that begin on the line. */
1355 for (vector<function_info *>::iterator it2 = fns->begin ();
1356 it2 != fns->end (); it2++)
1357 {
1358 if (!(*it2)->is_group)
1359 last_non_group_fns.push_back (x: *it2);
1360
1361 vector<line_info> &lines = (*it2)->lines;
1362 /* The LINES array is allocated only for group functions. */
1363 for (unsigned i = 0; i < lines.size (); i++)
1364 {
1365 line_info *line = &lines[i];
1366 output_intermediate_json_line (object: lineso, line, line_num: line_num + i,
1367 function_name: (*it2)->m_name);
1368 }
1369 }
1370
1371 /* Follow with lines associated with the source file. */
1372 if (line_num < src->lines.size ())
1373 {
1374 unsigned size = last_non_group_fns.size ();
1375 function_info *last_fn = size > 0 ? last_non_group_fns[size - 1] : NULL;
1376 const char *fname = last_fn ? last_fn->m_name : NULL;
1377 output_intermediate_json_line (object: lineso, line: &src->lines[line_num], line_num,
1378 function_name: fname);
1379
1380 /* Pop ending function from stack. */
1381 if (last_fn != NULL && last_fn->end_line == line_num)
1382 last_non_group_fns.pop_back ();
1383 }
1384 }
1385}
1386
1387/* Function start pair. */
1388struct function_start
1389{
1390 unsigned source_file_idx;
1391 unsigned start_line;
1392};
1393
1394/* Traits class for function start hash maps below. */
1395
1396struct function_start_pair_hash : typed_noop_remove <function_start>
1397{
1398 typedef function_start value_type;
1399 typedef function_start compare_type;
1400
1401 static hashval_t
1402 hash (const function_start &ref)
1403 {
1404 inchash::hash hstate (0);
1405 hstate.add_int (v: ref.source_file_idx);
1406 hstate.add_int (v: ref.start_line);
1407 return hstate.end ();
1408 }
1409
1410 static bool
1411 equal (const function_start &ref1, const function_start &ref2)
1412 {
1413 return (ref1.source_file_idx == ref2.source_file_idx
1414 && ref1.start_line == ref2.start_line);
1415 }
1416
1417 static void
1418 mark_deleted (function_start &ref)
1419 {
1420 ref.start_line = ~1U;
1421 }
1422
1423 static const bool empty_zero_p = false;
1424
1425 static void
1426 mark_empty (function_start &ref)
1427 {
1428 ref.start_line = ~2U;
1429 }
1430
1431 static bool
1432 is_deleted (const function_start &ref)
1433 {
1434 return ref.start_line == ~1U;
1435 }
1436
1437 static bool
1438 is_empty (const function_start &ref)
1439 {
1440 return ref.start_line == ~2U;
1441 }
1442};
1443
1444/* Process a single input file. */
1445
1446static void
1447process_file (const char *file_name)
1448{
1449 create_file_names (file_name);
1450
1451 for (unsigned i = 0; i < processed_files.size (); i++)
1452 if (strcmp (s1: da_file_name, s2: processed_files[i]) == 0)
1453 {
1454 fnotice (stderr, "'%s' file is already processed\n",
1455 file_name);
1456 return;
1457 }
1458
1459 processed_files.push_back (x: xstrdup (da_file_name));
1460
1461 read_graph_file ();
1462 read_count_file ();
1463}
1464
1465/* Process all functions in all files. */
1466
1467static void
1468process_all_functions (void)
1469{
1470 hash_map<function_start_pair_hash, function_info *> fn_map;
1471
1472 /* Identify group functions. */
1473 for (vector<function_info *>::iterator it = functions.begin ();
1474 it != functions.end (); it++)
1475 if (!(*it)->artificial)
1476 {
1477 function_start needle;
1478 needle.source_file_idx = (*it)->src;
1479 needle.start_line = (*it)->start_line;
1480
1481 function_info **slot = fn_map.get (k: needle);
1482 if (slot)
1483 {
1484 (*slot)->is_group = 1;
1485 (*it)->is_group = 1;
1486 }
1487 else
1488 fn_map.put (k: needle, v: *it);
1489 }
1490
1491 /* Remove all artificial function. */
1492 functions.erase (first: remove_if (first: functions.begin (), last: functions.end (),
1493 pred: function_info::is_artificial), last: functions.end ());
1494
1495 for (vector<function_info *>::iterator it = functions.begin ();
1496 it != functions.end (); it++)
1497 {
1498 function_info *fn = *it;
1499 unsigned src = fn->src;
1500
1501 if (!fn->counts.empty () || no_data_file)
1502 {
1503 source_info *s = &sources[src];
1504 s->add_function (fn);
1505
1506 /* Mark last line in files touched by function. */
1507 for (unsigned block_no = 0; block_no != fn->blocks.size ();
1508 block_no++)
1509 {
1510 block_info *block = &fn->blocks[block_no];
1511 for (unsigned i = 0; i < block->locations.size (); i++)
1512 {
1513 /* Sort lines of locations. */
1514 sort (first: block->locations[i].lines.begin (),
1515 last: block->locations[i].lines.end ());
1516
1517 if (!block->locations[i].lines.empty ())
1518 {
1519 s = &sources[block->locations[i].source_file_idx];
1520 unsigned last_line
1521 = block->locations[i].lines.back ();
1522
1523 /* Record new lines for the function. */
1524 if (last_line >= s->lines.size ())
1525 {
1526 s = &sources[block->locations[i].source_file_idx];
1527 unsigned last_line
1528 = block->locations[i].lines.back ();
1529
1530 /* Record new lines for the function. */
1531 if (last_line >= s->lines.size ())
1532 {
1533 /* Record new lines for a source file. */
1534 s->lines.resize (new_size: last_line + 1);
1535 }
1536 }
1537 }
1538 }
1539 }
1540
1541 /* Allocate lines for group function, following start_line
1542 and end_line information of the function. */
1543 if (fn->is_group)
1544 fn->lines.resize (new_size: fn->end_line - fn->start_line + 1);
1545
1546 solve_flow_graph (fn);
1547 if (fn->has_catch)
1548 find_exception_blocks (fn);
1549 }
1550 else
1551 {
1552 /* The function was not in the executable -- some other
1553 instance must have been selected. */
1554 }
1555 }
1556}
1557
1558static void
1559output_gcov_file (const char *file_name, source_info *src)
1560{
1561 string gcov_file_name_str
1562 = make_gcov_file_name (file_name, src->coverage.name);
1563 const char *gcov_file_name = gcov_file_name_str.c_str ();
1564
1565 if (src->coverage.lines)
1566 {
1567 FILE *gcov_file = fopen (filename: gcov_file_name, modes: "w");
1568 if (gcov_file)
1569 {
1570 fnotice (stdout, "Creating '%s'\n", gcov_file_name);
1571 output_lines (gcov_file, src);
1572 if (ferror (stream: gcov_file))
1573 {
1574 fnotice (stderr, "Error writing output file '%s'\n",
1575 gcov_file_name);
1576 return_code = 6;
1577 }
1578 fclose (stream: gcov_file);
1579 }
1580 else
1581 {
1582 fnotice (stderr, "Could not open output file '%s'\n", gcov_file_name);
1583 return_code = 6;
1584 }
1585 }
1586 else
1587 {
1588 unlink (name: gcov_file_name);
1589 fnotice (stdout, "Removing '%s'\n", gcov_file_name);
1590 }
1591}
1592
1593static void
1594generate_results (const char *file_name)
1595{
1596 string gcov_intermediate_filename;
1597
1598 for (vector<function_info *>::iterator it = functions.begin ();
1599 it != functions.end (); it++)
1600 {
1601 function_info *fn = *it;
1602 coverage_info coverage;
1603
1604 memset (s: &coverage, c: 0, n: sizeof (coverage));
1605 coverage.name = fn->get_name ();
1606 add_line_counts (flag_function_summary ? &coverage : NULL, fn);
1607 if (flag_function_summary)
1608 {
1609 function_summary (&coverage);
1610 fnotice (stdout, "\n");
1611 }
1612 }
1613
1614 name_map needle;
1615 needle.name = file_name;
1616 vector<name_map>::iterator it
1617 = std::find (first: names.begin (), last: names.end (), val: needle);
1618 if (it != names.end ())
1619 file_name = sources[it->src].coverage.name;
1620 else
1621 file_name = canonicalize_name (file_name);
1622
1623 gcov_intermediate_filename = get_gcov_intermediate_filename (input_file_name: file_name);
1624
1625 json::object *root = new json::object ();
1626 root->set_string (key: "format_version", GCOV_JSON_FORMAT_VERSION);
1627 root->set_string (key: "gcc_version", version_string);
1628
1629 if (bbg_cwd != NULL)
1630 root->set_string (key: "current_working_directory", utf8_value: bbg_cwd);
1631 root->set_string (key: "data_file", utf8_value: file_name);
1632
1633 json::array *json_files = new json::array ();
1634 root->set (key: "files", v: json_files);
1635
1636 for (vector<source_info>::iterator it = sources.begin ();
1637 it != sources.end (); it++)
1638 {
1639 source_info *src = &(*it);
1640 if (flag_relative_only)
1641 {
1642 /* Ignore this source, if it is an absolute path (after
1643 source prefix removal). */
1644 char first = src->coverage.name[0];
1645
1646#if HAVE_DOS_BASED_FILE_SYSTEM
1647 if (first && src->coverage.name[1] == ':')
1648 first = src->coverage.name[2];
1649#endif
1650 if (IS_DIR_SEPARATOR (first))
1651 continue;
1652 }
1653
1654 accumulate_line_counts (src);
1655 if (flag_debug)
1656 src->debug ();
1657
1658 if (!flag_use_stdout)
1659 file_summary (&src->coverage);
1660 total_lines += src->coverage.lines;
1661 total_executed += src->coverage.lines_executed;
1662 if (flag_gcov_file)
1663 {
1664 if (flag_json_format)
1665 {
1666 output_json_intermediate_file (json_files, src);
1667 if (!flag_use_stdout)
1668 fnotice (stdout, "\n");
1669 }
1670 else
1671 {
1672 if (flag_use_stdout)
1673 {
1674 if (src->coverage.lines)
1675 output_lines (stdout, src);
1676 }
1677 else
1678 {
1679 output_gcov_file (file_name, src);
1680 fnotice (stdout, "\n");
1681 }
1682 }
1683 }
1684 }
1685
1686 if (flag_gcov_file && flag_json_format)
1687 {
1688 if (flag_use_stdout)
1689 {
1690 root->dump (stdout, formatted: false);
1691 printf (format: "\n");
1692 }
1693 else
1694 {
1695 pretty_printer pp;
1696 root->print (pp: &pp, formatted: false);
1697 pp_formatted_text (&pp);
1698
1699 fnotice (stdout, "Creating '%s'\n",
1700 gcov_intermediate_filename.c_str ());
1701 gzFile output = gzopen (gcov_intermediate_filename.c_str (), "w");
1702 if (output == NULL)
1703 {
1704 fnotice (stderr, "Cannot open JSON output file %s\n",
1705 gcov_intermediate_filename.c_str ());
1706 return_code = 6;
1707 return;
1708 }
1709
1710 if (gzputs (file: output, s: pp_formatted_text (&pp)) == EOF
1711 || gzclose (file: output))
1712 {
1713 fnotice (stderr, "Error writing JSON output file %s\n",
1714 gcov_intermediate_filename.c_str ());
1715 return_code = 6;
1716 return;
1717 }
1718 }
1719 }
1720}
1721
1722/* Release all memory used. */
1723
1724static void
1725release_structures (void)
1726{
1727 for (vector<function_info *>::iterator it = functions.begin ();
1728 it != functions.end (); it++)
1729 delete (*it);
1730
1731 sources.resize (new_size: 0);
1732 names.resize (new_size: 0);
1733 functions.resize (new_size: 0);
1734 ident_to_fn.clear ();
1735}
1736
1737/* Generate the names of the graph and data files. If OBJECT_DIRECTORY
1738 is not specified, these are named from FILE_NAME sans extension. If
1739 OBJECT_DIRECTORY is specified and is a directory, the files are in that
1740 directory, but named from the basename of the FILE_NAME, sans extension.
1741 Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file*
1742 and the data files are named from that. */
1743
1744static void
1745create_file_names (const char *file_name)
1746{
1747 char *cptr;
1748 char *name;
1749 int length = strlen (s: file_name);
1750 int base;
1751
1752 /* Free previous file names. */
1753 free (ptr: bbg_file_name);
1754 free (ptr: da_file_name);
1755 da_file_name = bbg_file_name = NULL;
1756 bbg_file_time = 0;
1757 bbg_stamp = 0;
1758
1759 if (object_directory && object_directory[0])
1760 {
1761 struct stat status;
1762
1763 length += strlen (s: object_directory) + 2;
1764 name = XNEWVEC (char, length);
1765 name[0] = 0;
1766
1767 base = !stat (file: object_directory, buf: &status) && S_ISDIR (status.st_mode);
1768 strcat (dest: name, src: object_directory);
1769 if (base && (!IS_DIR_SEPARATOR (name[strlen (name) - 1])))
1770 strcat (dest: name, src: "/");
1771 }
1772 else
1773 {
1774 name = XNEWVEC (char, length + 1);
1775 strcpy (dest: name, src: file_name);
1776 base = 0;
1777 }
1778
1779 if (base)
1780 {
1781 /* Append source file name. */
1782 const char *cptr = lbasename (file_name);
1783 strcat (dest: name, src: cptr ? cptr : file_name);
1784 }
1785
1786 /* Remove the extension. */
1787 cptr = strrchr (CONST_CAST (char *, lbasename (name)), c: '.');
1788 if (cptr)
1789 *cptr = 0;
1790
1791 length = strlen (s: name);
1792
1793 bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
1794 strcpy (dest: bbg_file_name, src: name);
1795 strcpy (dest: bbg_file_name + length, GCOV_NOTE_SUFFIX);
1796
1797 da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
1798 strcpy (dest: da_file_name, src: name);
1799 strcpy (dest: da_file_name + length, GCOV_DATA_SUFFIX);
1800
1801 free (ptr: name);
1802 return;
1803}
1804
1805/* Find or create a source file structure for FILE_NAME. Copies
1806 FILE_NAME on creation */
1807
1808static unsigned
1809find_source (const char *file_name)
1810{
1811 char *canon;
1812 unsigned idx;
1813 struct stat status;
1814
1815 if (!file_name)
1816 file_name = "<unknown>";
1817
1818 name_map needle;
1819 needle.name = file_name;
1820
1821 vector<name_map>::iterator it = std::find (first: names.begin (), last: names.end (),
1822 val: needle);
1823 if (it != names.end ())
1824 {
1825 idx = it->src;
1826 goto check_date;
1827 }
1828
1829 /* Not found, try the canonical name. */
1830 canon = canonicalize_name (file_name);
1831 needle.name = canon;
1832 it = std::find (first: names.begin (), last: names.end (), val: needle);
1833 if (it == names.end ())
1834 {
1835 /* Not found with canonical name, create a new source. */
1836 source_info *src;
1837
1838 idx = sources.size ();
1839 needle = name_map (canon, idx);
1840 names.push_back (x: needle);
1841
1842 sources.push_back (x: source_info ());
1843 src = &sources.back ();
1844 src->name = canon;
1845 src->coverage.name = src->name;
1846 src->index = idx;
1847 if (source_length
1848#if HAVE_DOS_BASED_FILE_SYSTEM
1849 /* You lose if separators don't match exactly in the
1850 prefix. */
1851 && !strncasecmp (source_prefix, src->coverage.name, source_length)
1852#else
1853 && !strncmp (s1: source_prefix, s2: src->coverage.name, n: source_length)
1854#endif
1855 && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
1856 src->coverage.name += source_length + 1;
1857 if (!stat (file: src->name, buf: &status))
1858 src->file_time = status.st_mtime;
1859 }
1860 else
1861 idx = it->src;
1862
1863 needle.name = file_name;
1864 if (std::find (first: names.begin (), last: names.end (), val: needle) == names.end ())
1865 {
1866 /* Append the non-canonical name. */
1867 names.push_back (x: name_map (xstrdup (file_name), idx));
1868 }
1869
1870 /* Resort the name map. */
1871 std::sort (first: names.begin (), last: names.end ());
1872
1873 check_date:
1874 if (sources[idx].file_time > bbg_file_time)
1875 {
1876 static int info_emitted;
1877
1878 fnotice (stderr, "%s:source file is newer than notes file '%s'\n",
1879 file_name, bbg_file_name);
1880 if (!info_emitted)
1881 {
1882 fnotice (stderr,
1883 "(the message is displayed only once per source file)\n");
1884 info_emitted = 1;
1885 }
1886 sources[idx].file_time = 0;
1887 }
1888
1889 return idx;
1890}
1891
1892/* Read the notes file. Save functions to FUNCTIONS global vector. */
1893
1894static void
1895read_graph_file (void)
1896{
1897 unsigned version;
1898 unsigned current_tag = 0;
1899 unsigned tag;
1900
1901 if (!gcov_open (name: bbg_file_name, mode: 1))
1902 {
1903 fnotice (stderr, "%s:cannot open notes file\n", bbg_file_name);
1904 return_code = 1;
1905 return;
1906 }
1907 bbg_file_time = gcov_time ();
1908 if (!gcov_magic (magic: gcov_read_unsigned (), GCOV_NOTE_MAGIC))
1909 {
1910 fnotice (stderr, "%s:not a gcov notes file\n", bbg_file_name);
1911 return_code = 2;
1912 gcov_close ();
1913 return;
1914 }
1915
1916 version = gcov_read_unsigned ();
1917 if (version != GCOV_VERSION)
1918 {
1919 char v[4], e[4];
1920
1921 GCOV_UNSIGNED2STRING (v, version);
1922 GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
1923
1924 fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
1925 bbg_file_name, v, e);
1926 return_code = 3;
1927 }
1928 bbg_stamp = gcov_read_unsigned ();
1929 /* Read checksum. */
1930 gcov_read_unsigned ();
1931 bbg_cwd = xstrdup (gcov_read_string ());
1932 bbg_supports_has_unexecuted_blocks = gcov_read_unsigned ();
1933
1934 function_info *fn = NULL;
1935 while ((tag = gcov_read_unsigned ()))
1936 {
1937 unsigned length = gcov_read_unsigned ();
1938 gcov_position_t base = gcov_position ();
1939
1940 if (tag == GCOV_TAG_FUNCTION)
1941 {
1942 char *function_name;
1943 unsigned ident;
1944 unsigned lineno_checksum, cfg_checksum;
1945
1946 ident = gcov_read_unsigned ();
1947 lineno_checksum = gcov_read_unsigned ();
1948 cfg_checksum = gcov_read_unsigned ();
1949 function_name = xstrdup (gcov_read_string ());
1950 unsigned artificial = gcov_read_unsigned ();
1951 unsigned src_idx = find_source (file_name: gcov_read_string ());
1952 unsigned start_line = gcov_read_unsigned ();
1953 unsigned start_column = gcov_read_unsigned ();
1954 unsigned end_line = gcov_read_unsigned ();
1955 unsigned end_column = gcov_read_unsigned ();
1956
1957 fn = new function_info ();
1958 functions.push_back (x: fn);
1959 ident_to_fn[ident] = fn;
1960
1961 fn->m_name = function_name;
1962 fn->ident = ident;
1963 fn->lineno_checksum = lineno_checksum;
1964 fn->cfg_checksum = cfg_checksum;
1965 fn->src = src_idx;
1966 fn->start_line = start_line;
1967 fn->start_column = start_column;
1968 fn->end_line = end_line;
1969 fn->end_column = end_column;
1970 fn->artificial = artificial;
1971
1972 current_tag = tag;
1973 }
1974 else if (fn && tag == GCOV_TAG_BLOCKS)
1975 {
1976 if (!fn->blocks.empty ())
1977 fnotice (stderr, "%s:already seen blocks for '%s'\n",
1978 bbg_file_name, fn->get_name ());
1979 else
1980 fn->blocks.resize (new_size: gcov_read_unsigned ());
1981 }
1982 else if (fn && tag == GCOV_TAG_ARCS)
1983 {
1984 unsigned src = gcov_read_unsigned ();
1985 fn->blocks[src].id = src;
1986 unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
1987 block_info *src_blk = &fn->blocks[src];
1988 unsigned mark_catches = 0;
1989 struct arc_info *arc;
1990
1991 if (src >= fn->blocks.size () || fn->blocks[src].succ)
1992 goto corrupt;
1993
1994 while (num_dests--)
1995 {
1996 unsigned dest = gcov_read_unsigned ();
1997 unsigned flags = gcov_read_unsigned ();
1998
1999 if (dest >= fn->blocks.size ())
2000 goto corrupt;
2001 arc = XCNEW (arc_info);
2002
2003 arc->dst = &fn->blocks[dest];
2004 /* Set id in order to find EXIT_BLOCK. */
2005 arc->dst->id = dest;
2006 arc->src = src_blk;
2007
2008 arc->count = 0;
2009 arc->count_valid = 0;
2010 arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
2011 arc->fake = !!(flags & GCOV_ARC_FAKE);
2012 arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
2013
2014 arc->succ_next = src_blk->succ;
2015 src_blk->succ = arc;
2016 src_blk->num_succ++;
2017
2018 arc->pred_next = fn->blocks[dest].pred;
2019 fn->blocks[dest].pred = arc;
2020 fn->blocks[dest].num_pred++;
2021
2022 if (arc->fake)
2023 {
2024 if (src)
2025 {
2026 /* Exceptional exit from this function, the
2027 source block must be a call. */
2028 fn->blocks[src].is_call_site = 1;
2029 arc->is_call_non_return = 1;
2030 mark_catches = 1;
2031 }
2032 else
2033 {
2034 /* Non-local return from a callee of this
2035 function. The destination block is a setjmp. */
2036 arc->is_nonlocal_return = 1;
2037 fn->blocks[dest].is_nonlocal_return = 1;
2038 }
2039 }
2040
2041 if (!arc->on_tree)
2042 fn->counts.push_back (x: 0);
2043 }
2044
2045 if (mark_catches)
2046 {
2047 /* We have a fake exit from this block. The other
2048 non-fall through exits must be to catch handlers.
2049 Mark them as catch arcs. */
2050
2051 for (arc = src_blk->succ; arc; arc = arc->succ_next)
2052 if (!arc->fake && !arc->fall_through)
2053 {
2054 arc->is_throw = 1;
2055 fn->has_catch = 1;
2056 }
2057 }
2058 }
2059 else if (fn && tag == GCOV_TAG_CONDS)
2060 {
2061 unsigned num_dests = GCOV_TAG_CONDS_NUM (length);
2062
2063 if (!fn->conditions.empty ())
2064 fnotice (stderr, "%s:already seen conditions for '%s'\n",
2065 bbg_file_name, fn->get_name ());
2066 else
2067 fn->conditions.resize (new_size: num_dests);
2068
2069 for (unsigned i = 0; i < num_dests; ++i)
2070 {
2071 unsigned idx = gcov_read_unsigned ();
2072
2073 if (idx >= fn->blocks.size ())
2074 goto corrupt;
2075
2076 condition_info *info = &fn->blocks[idx].conditions;
2077 info->n_terms = gcov_read_unsigned ();
2078 fn->conditions[i] = info;
2079 }
2080 }
2081 else if (fn && tag == GCOV_TAG_LINES)
2082 {
2083 unsigned blockno = gcov_read_unsigned ();
2084 block_info *block = &fn->blocks[blockno];
2085
2086 if (blockno >= fn->blocks.size ())
2087 goto corrupt;
2088
2089 while (true)
2090 {
2091 unsigned lineno = gcov_read_unsigned ();
2092
2093 if (lineno)
2094 block->locations.back ().lines.push_back (x: lineno);
2095 else
2096 {
2097 const char *file_name = gcov_read_string ();
2098
2099 if (!file_name)
2100 break;
2101 block->locations.push_back (x: block_location_info
2102 (find_source (file_name)));
2103 }
2104 }
2105 }
2106 else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
2107 {
2108 fn = NULL;
2109 current_tag = 0;
2110 }
2111 gcov_sync (base, length);
2112 if (gcov_is_error ())
2113 {
2114 corrupt:;
2115 fnotice (stderr, "%s:corrupted\n", bbg_file_name);
2116 return_code = 4;
2117 break;
2118 }
2119 }
2120 gcov_close ();
2121
2122 if (functions.empty ())
2123 fnotice (stderr, "%s:no functions found\n", bbg_file_name);
2124}
2125
2126/* Reads profiles from the count file and attach to each
2127 function. Return nonzero if fatal error. */
2128
2129static int
2130read_count_file (void)
2131{
2132 unsigned ix;
2133 unsigned version;
2134 unsigned tag;
2135 function_info *fn = NULL;
2136 int error = 0;
2137 map<unsigned, function_info *>::iterator it;
2138
2139 if (!gcov_open (name: da_file_name, mode: 1))
2140 {
2141 fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
2142 da_file_name);
2143 no_data_file = 1;
2144 return 0;
2145 }
2146 if (!gcov_magic (magic: gcov_read_unsigned (), GCOV_DATA_MAGIC))
2147 {
2148 fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
2149 return_code = 2;
2150 cleanup:;
2151 gcov_close ();
2152 return 1;
2153 }
2154 version = gcov_read_unsigned ();
2155 if (version != GCOV_VERSION)
2156 {
2157 char v[4], e[4];
2158
2159 GCOV_UNSIGNED2STRING (v, version);
2160 GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
2161
2162 fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
2163 da_file_name, v, e);
2164 return_code = 3;
2165 }
2166 tag = gcov_read_unsigned ();
2167 if (tag != bbg_stamp)
2168 {
2169 fnotice (stderr, "%s:stamp mismatch with notes file\n", da_file_name);
2170 return_code = 5;
2171 goto cleanup;
2172 }
2173
2174 /* Read checksum. */
2175 gcov_read_unsigned ();
2176
2177 while ((tag = gcov_read_unsigned ()))
2178 {
2179 unsigned length = gcov_read_unsigned ();
2180 int read_length = (int)length;
2181 unsigned long base = gcov_position ();
2182
2183 if (tag == GCOV_TAG_OBJECT_SUMMARY)
2184 {
2185 struct gcov_summary summary;
2186 gcov_read_summary (summary: &summary);
2187 object_runs = summary.runs;
2188 }
2189 else if (tag == GCOV_TAG_FUNCTION && !length)
2190 ; /* placeholder */
2191 else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
2192 {
2193 unsigned ident;
2194 ident = gcov_read_unsigned ();
2195 fn = NULL;
2196 it = ident_to_fn.find (x: ident);
2197 if (it != ident_to_fn.end ())
2198 fn = it->second;
2199
2200 if (!fn)
2201 ;
2202 else if (gcov_read_unsigned () != fn->lineno_checksum
2203 || gcov_read_unsigned () != fn->cfg_checksum)
2204 {
2205 mismatch:;
2206 fnotice (stderr, "%s:profile mismatch for '%s'\n",
2207 da_file_name, fn->get_name ());
2208 goto cleanup;
2209 }
2210 }
2211 else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_CONDS) && fn)
2212 {
2213 length = abs (x: read_length);
2214 if (length != GCOV_TAG_COUNTER_LENGTH (2 * fn->conditions.size ()))
2215 goto mismatch;
2216
2217 if (read_length > 0)
2218 {
2219 for (ix = 0; ix != fn->conditions.size (); ix++)
2220 {
2221 fn->conditions[ix]->truev |= gcov_read_counter ();
2222 fn->conditions[ix]->falsev |= gcov_read_counter ();
2223 }
2224 }
2225 }
2226 else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
2227 {
2228 length = abs (x: read_length);
2229 if (length != GCOV_TAG_COUNTER_LENGTH (fn->counts.size ()))
2230 goto mismatch;
2231
2232 if (read_length > 0)
2233 for (ix = 0; ix != fn->counts.size (); ix++)
2234 fn->counts[ix] += gcov_read_counter ();
2235 }
2236 if (read_length < 0)
2237 read_length = 0;
2238 gcov_sync (base, length: read_length);
2239 if ((error = gcov_is_error ()))
2240 {
2241 fnotice (stderr,
2242 error < 0
2243 ? N_("%s:overflowed\n")
2244 : N_("%s:corrupted\n"),
2245 da_file_name);
2246 return_code = 4;
2247 goto cleanup;
2248 }
2249 }
2250
2251 gcov_close ();
2252 return 0;
2253}
2254
2255/* Solve the flow graph. Propagate counts from the instrumented arcs
2256 to the blocks and the uninstrumented arcs. */
2257
2258static void
2259solve_flow_graph (function_info *fn)
2260{
2261 unsigned ix;
2262 arc_info *arc;
2263 gcov_type *count_ptr = &fn->counts.front ();
2264 block_info *blk;
2265 block_info *valid_blocks = NULL; /* valid, but unpropagated blocks. */
2266 block_info *invalid_blocks = NULL; /* invalid, but inferable blocks. */
2267
2268 /* The arcs were built in reverse order. Fix that now. */
2269 for (ix = fn->blocks.size (); ix--;)
2270 {
2271 arc_info *arc_p, *arc_n;
2272
2273 for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
2274 arc_p = arc, arc = arc_n)
2275 {
2276 arc_n = arc->succ_next;
2277 arc->succ_next = arc_p;
2278 }
2279 fn->blocks[ix].succ = arc_p;
2280
2281 for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
2282 arc_p = arc, arc = arc_n)
2283 {
2284 arc_n = arc->pred_next;
2285 arc->pred_next = arc_p;
2286 }
2287 fn->blocks[ix].pred = arc_p;
2288 }
2289
2290 if (fn->blocks.size () < 2)
2291 fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
2292 bbg_file_name, fn->get_name ());
2293 else
2294 {
2295 if (fn->blocks[ENTRY_BLOCK].num_pred)
2296 fnotice (stderr, "%s:'%s' has arcs to entry block\n",
2297 bbg_file_name, fn->get_name ());
2298 else
2299 /* We can't deduce the entry block counts from the lack of
2300 predecessors. */
2301 fn->blocks[ENTRY_BLOCK].num_pred = ~(unsigned)0;
2302
2303 if (fn->blocks[EXIT_BLOCK].num_succ)
2304 fnotice (stderr, "%s:'%s' has arcs from exit block\n",
2305 bbg_file_name, fn->get_name ());
2306 else
2307 /* Likewise, we can't deduce exit block counts from the lack
2308 of its successors. */
2309 fn->blocks[EXIT_BLOCK].num_succ = ~(unsigned)0;
2310 }
2311
2312 /* Propagate the measured counts, this must be done in the same
2313 order as the code in profile.cc */
2314 for (unsigned i = 0; i < fn->blocks.size (); i++)
2315 {
2316 blk = &fn->blocks[i];
2317 block_info const *prev_dst = NULL;
2318 int out_of_order = 0;
2319 int non_fake_succ = 0;
2320
2321 for (arc = blk->succ; arc; arc = arc->succ_next)
2322 {
2323 if (!arc->fake)
2324 non_fake_succ++;
2325
2326 if (!arc->on_tree)
2327 {
2328 if (count_ptr)
2329 arc->count = *count_ptr++;
2330 arc->count_valid = 1;
2331 blk->num_succ--;
2332 arc->dst->num_pred--;
2333 }
2334 if (prev_dst && prev_dst > arc->dst)
2335 out_of_order = 1;
2336 prev_dst = arc->dst;
2337 }
2338 if (non_fake_succ == 1)
2339 {
2340 /* If there is only one non-fake exit, it is an
2341 unconditional branch. */
2342 for (arc = blk->succ; arc; arc = arc->succ_next)
2343 if (!arc->fake)
2344 {
2345 arc->is_unconditional = 1;
2346 /* If this block is instrumenting a call, it might be
2347 an artificial block. It is not artificial if it has
2348 a non-fallthrough exit, or the destination of this
2349 arc has more than one entry. Mark the destination
2350 block as a return site, if none of those conditions
2351 hold. */
2352 if (blk->is_call_site && arc->fall_through
2353 && arc->dst->pred == arc && !arc->pred_next)
2354 arc->dst->is_call_return = 1;
2355 }
2356 }
2357
2358 /* Sort the successor arcs into ascending dst order. profile.cc
2359 normally produces arcs in the right order, but sometimes with
2360 one or two out of order. We're not using a particularly
2361 smart sort. */
2362 if (out_of_order)
2363 {
2364 arc_info *start = blk->succ;
2365 unsigned changes = 1;
2366
2367 while (changes)
2368 {
2369 arc_info *arc, *arc_p, *arc_n;
2370
2371 changes = 0;
2372 for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
2373 {
2374 if (arc->dst > arc_n->dst)
2375 {
2376 changes = 1;
2377 if (arc_p)
2378 arc_p->succ_next = arc_n;
2379 else
2380 start = arc_n;
2381 arc->succ_next = arc_n->succ_next;
2382 arc_n->succ_next = arc;
2383 arc_p = arc_n;
2384 }
2385 else
2386 {
2387 arc_p = arc;
2388 arc = arc_n;
2389 }
2390 }
2391 }
2392 blk->succ = start;
2393 }
2394
2395 /* Place it on the invalid chain, it will be ignored if that's
2396 wrong. */
2397 blk->invalid_chain = 1;
2398 blk->chain = invalid_blocks;
2399 invalid_blocks = blk;
2400 }
2401
2402 while (invalid_blocks || valid_blocks)
2403 {
2404 while ((blk = invalid_blocks))
2405 {
2406 gcov_type total = 0;
2407 const arc_info *arc;
2408
2409 invalid_blocks = blk->chain;
2410 blk->invalid_chain = 0;
2411 if (!blk->num_succ)
2412 for (arc = blk->succ; arc; arc = arc->succ_next)
2413 total += arc->count;
2414 else if (!blk->num_pred)
2415 for (arc = blk->pred; arc; arc = arc->pred_next)
2416 total += arc->count;
2417 else
2418 continue;
2419
2420 blk->count = total;
2421 blk->count_valid = 1;
2422 blk->chain = valid_blocks;
2423 blk->valid_chain = 1;
2424 valid_blocks = blk;
2425 }
2426 while ((blk = valid_blocks))
2427 {
2428 gcov_type total;
2429 arc_info *arc, *inv_arc;
2430
2431 valid_blocks = blk->chain;
2432 blk->valid_chain = 0;
2433 if (blk->num_succ == 1)
2434 {
2435 block_info *dst;
2436
2437 total = blk->count;
2438 inv_arc = NULL;
2439 for (arc = blk->succ; arc; arc = arc->succ_next)
2440 {
2441 total -= arc->count;
2442 if (!arc->count_valid)
2443 inv_arc = arc;
2444 }
2445 dst = inv_arc->dst;
2446 inv_arc->count_valid = 1;
2447 inv_arc->count = total;
2448 blk->num_succ--;
2449 dst->num_pred--;
2450 if (dst->count_valid)
2451 {
2452 if (dst->num_pred == 1 && !dst->valid_chain)
2453 {
2454 dst->chain = valid_blocks;
2455 dst->valid_chain = 1;
2456 valid_blocks = dst;
2457 }
2458 }
2459 else
2460 {
2461 if (!dst->num_pred && !dst->invalid_chain)
2462 {
2463 dst->chain = invalid_blocks;
2464 dst->invalid_chain = 1;
2465 invalid_blocks = dst;
2466 }
2467 }
2468 }
2469 if (blk->num_pred == 1)
2470 {
2471 block_info *src;
2472
2473 total = blk->count;
2474 inv_arc = NULL;
2475 for (arc = blk->pred; arc; arc = arc->pred_next)
2476 {
2477 total -= arc->count;
2478 if (!arc->count_valid)
2479 inv_arc = arc;
2480 }
2481 src = inv_arc->src;
2482 inv_arc->count_valid = 1;
2483 inv_arc->count = total;
2484 blk->num_pred--;
2485 src->num_succ--;
2486 if (src->count_valid)
2487 {
2488 if (src->num_succ == 1 && !src->valid_chain)
2489 {
2490 src->chain = valid_blocks;
2491 src->valid_chain = 1;
2492 valid_blocks = src;
2493 }
2494 }
2495 else
2496 {
2497 if (!src->num_succ && !src->invalid_chain)
2498 {
2499 src->chain = invalid_blocks;
2500 src->invalid_chain = 1;
2501 invalid_blocks = src;
2502 }
2503 }
2504 }
2505 }
2506 }
2507
2508 /* If the graph has been correctly solved, every block will have a
2509 valid count. */
2510 for (unsigned i = 0; ix < fn->blocks.size (); i++)
2511 if (!fn->blocks[i].count_valid)
2512 {
2513 fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
2514 bbg_file_name, fn->get_name ());
2515 break;
2516 }
2517}
2518
2519/* Mark all the blocks only reachable via an incoming catch. */
2520
2521static void
2522find_exception_blocks (function_info *fn)
2523{
2524 unsigned ix;
2525 block_info **queue = XALLOCAVEC (block_info *, fn->blocks.size ());
2526
2527 /* First mark all blocks as exceptional. */
2528 for (ix = fn->blocks.size (); ix--;)
2529 fn->blocks[ix].exceptional = 1;
2530
2531 /* Now mark all the blocks reachable via non-fake edges */
2532 queue[0] = &fn->blocks[0];
2533 queue[0]->exceptional = 0;
2534 for (ix = 1; ix;)
2535 {
2536 block_info *block = queue[--ix];
2537 const arc_info *arc;
2538
2539 for (arc = block->succ; arc; arc = arc->succ_next)
2540 if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
2541 {
2542 arc->dst->exceptional = 0;
2543 queue[ix++] = arc->dst;
2544 }
2545 }
2546}
2547
2548
2549/* Increment totals in COVERAGE according to arc ARC. */
2550
2551static void
2552add_branch_counts (coverage_info *coverage, const arc_info *arc)
2553{
2554 if (arc->is_call_non_return)
2555 {
2556 coverage->calls++;
2557 if (arc->src->count)
2558 coverage->calls_executed++;
2559 }
2560 else if (!arc->is_unconditional)
2561 {
2562 coverage->branches++;
2563 if (arc->src->count)
2564 coverage->branches_executed++;
2565 if (arc->count)
2566 coverage->branches_taken++;
2567 }
2568}
2569
2570/* Increment totals in COVERAGE according to block BLOCK. */
2571
2572static void
2573add_condition_counts (coverage_info *coverage, const block_info *block)
2574{
2575 coverage->conditions += 2 * block->conditions.n_terms;
2576 coverage->conditions_covered += block->conditions.popcount ();
2577}
2578
2579/* Format COUNT, if flag_human_readable_numbers is set, return it human
2580 readable format. */
2581
2582static char const *
2583format_count (gcov_type count)
2584{
2585 static char buffer[64];
2586 const char *units = " kMGTPEZY";
2587
2588 if (count < 1000 || !flag_human_readable_numbers)
2589 {
2590 sprintf (s: buffer, format: "%" PRId64, count);
2591 return buffer;
2592 }
2593
2594 unsigned i;
2595 gcov_type divisor = 1;
2596 for (i = 0; units[i+1]; i++, divisor *= 1000)
2597 {
2598 if (count + divisor / 2 < 1000 * divisor)
2599 break;
2600 }
2601 float r = 1.0f * count / divisor;
2602 sprintf (s: buffer, format: "%.1f%c", r, units[i]);
2603 return buffer;
2604}
2605
2606/* Format a GCOV_TYPE integer as either a percent ratio, or absolute
2607 count. If DECIMAL_PLACES >= 0, format TOP/BOTTOM * 100 to DECIMAL_PLACES.
2608 If DECIMAL_PLACES is zero, no decimal point is printed. Only print 100% when
2609 TOP==BOTTOM and only print 0% when TOP=0. If DECIMAL_PLACES < 0, then simply
2610 format TOP. Return pointer to a static string. */
2611
2612static char const *
2613format_gcov (gcov_type top, gcov_type bottom, int decimal_places)
2614{
2615 static char buffer[20];
2616
2617 if (decimal_places >= 0)
2618 {
2619 float ratio = bottom ? 100.0f * top / bottom: 0;
2620
2621 /* Round up to 1% if there's a small non-zero value. */
2622 if (ratio > 0.0f && ratio < 0.5f && decimal_places == 0)
2623 ratio = 1.0f;
2624 sprintf (s: buffer, format: "%.*f%%", decimal_places, ratio);
2625 }
2626 else
2627 return format_count (count: top);
2628
2629 return buffer;
2630}
2631
2632/* Summary of execution */
2633
2634static void
2635executed_summary (unsigned lines, unsigned executed)
2636{
2637 if (lines)
2638 fnotice (stdout, "Lines executed:%s of %d\n",
2639 format_gcov (top: executed, bottom: lines, decimal_places: 2), lines);
2640 else
2641 fnotice (stdout, "No executable lines\n");
2642}
2643
2644/* Output summary info for a function. */
2645
2646static void
2647function_summary (const coverage_info *coverage)
2648{
2649 fnotice (stdout, "%s '%s'\n", "Function", coverage->name);
2650 executed_summary (lines: coverage->lines, executed: coverage->lines_executed);
2651}
2652
2653/* Output summary info for a file. */
2654
2655static void
2656file_summary (const coverage_info *coverage)
2657{
2658 fnotice (stdout, "%s '%s'\n", "File", coverage->name);
2659 executed_summary (lines: coverage->lines, executed: coverage->lines_executed);
2660
2661 if (flag_branches)
2662 {
2663 if (coverage->branches)
2664 {
2665 fnotice (stdout, "Branches executed:%s of %d\n",
2666 format_gcov (top: coverage->branches_executed,
2667 bottom: coverage->branches, decimal_places: 2),
2668 coverage->branches);
2669 fnotice (stdout, "Taken at least once:%s of %d\n",
2670 format_gcov (top: coverage->branches_taken,
2671 bottom: coverage->branches, decimal_places: 2),
2672 coverage->branches);
2673 }
2674 else
2675 fnotice (stdout, "No branches\n");
2676 if (coverage->calls)
2677 fnotice (stdout, "Calls executed:%s of %d\n",
2678 format_gcov (top: coverage->calls_executed, bottom: coverage->calls, decimal_places: 2),
2679 coverage->calls);
2680 else
2681 fnotice (stdout, "No calls\n");
2682
2683 }
2684
2685 if (flag_conditions)
2686 {
2687 if (coverage->conditions)
2688 fnotice (stdout, "Condition outcomes covered:%s of %d\n",
2689 format_gcov (top: coverage->conditions_covered,
2690 bottom: coverage->conditions, decimal_places: 2),
2691 coverage->conditions);
2692 else
2693 fnotice (stdout, "No conditions\n");
2694 }
2695}
2696
2697/* Canonicalize the filename NAME by canonicalizing directory
2698 separators, eliding . components and resolving .. components
2699 appropriately. Always returns a unique string. */
2700
2701static char *
2702canonicalize_name (const char *name)
2703{
2704 /* The canonical name cannot be longer than the incoming name. */
2705 char *result = XNEWVEC (char, strlen (name) + 1);
2706 const char *base = name, *probe;
2707 char *ptr = result;
2708 char *dd_base;
2709 int slash = 0;
2710
2711#if HAVE_DOS_BASED_FILE_SYSTEM
2712 if (base[0] && base[1] == ':')
2713 {
2714 result[0] = base[0];
2715 result[1] = ':';
2716 base += 2;
2717 ptr += 2;
2718 }
2719#endif
2720 for (dd_base = ptr; *base; base = probe)
2721 {
2722 size_t len;
2723
2724 for (probe = base; *probe; probe++)
2725 if (IS_DIR_SEPARATOR (*probe))
2726 break;
2727
2728 len = probe - base;
2729 if (len == 1 && base[0] == '.')
2730 /* Elide a '.' directory */
2731 ;
2732 else if (len == 2 && base[0] == '.' && base[1] == '.')
2733 {
2734 /* '..', we can only elide it and the previous directory, if
2735 we're not a symlink. */
2736 struct stat ATTRIBUTE_UNUSED buf;
2737
2738 *ptr = 0;
2739 if (dd_base == ptr
2740#if defined (S_ISLNK)
2741 /* S_ISLNK is not POSIX.1-1996. */
2742 || stat (file: result, buf: &buf) || S_ISLNK (buf.st_mode)
2743#endif
2744 )
2745 {
2746 /* Cannot elide, or unreadable or a symlink. */
2747 dd_base = ptr + 2 + slash;
2748 goto regular;
2749 }
2750 while (ptr != dd_base && *ptr != '/')
2751 ptr--;
2752 slash = ptr != result;
2753 }
2754 else
2755 {
2756 regular:
2757 /* Regular pathname component. */
2758 if (slash)
2759 *ptr++ = '/';
2760 memcpy (dest: ptr, src: base, n: len);
2761 ptr += len;
2762 slash = 1;
2763 }
2764
2765 for (; IS_DIR_SEPARATOR (*probe); probe++)
2766 continue;
2767 }
2768 *ptr = 0;
2769
2770 return result;
2771}
2772
2773/* Generate an output file name. INPUT_NAME is the canonicalized main
2774 input file and SRC_NAME is the canonicalized file name.
2775 LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation. With
2776 long_output_names we prepend the processed name of the input file
2777 to each output name (except when the current source file is the
2778 input file, so you don't get a double concatenation). The two
2779 components are separated by '##'. With preserve_paths we create a
2780 filename from all path components of the source file, replacing '/'
2781 with '#', and .. with '^', without it we simply take the basename
2782 component. (Remember, the canonicalized name will already have
2783 elided '.' components and converted \\ separators.) */
2784
2785static string
2786make_gcov_file_name (const char *input_name, const char *src_name)
2787{
2788 string str;
2789
2790 /* When hashing filenames, we shorten them by only using the filename
2791 component and appending a hash of the full (mangled) pathname. */
2792 if (flag_hash_filenames)
2793 str = (string (mangle_name (src_name)) + "##"
2794 + get_md5sum (input: src_name) + ".gcov");
2795 else
2796 {
2797 if (flag_long_names && input_name && strcmp (s1: src_name, s2: input_name) != 0)
2798 {
2799 str += mangle_name (input_name);
2800 str += "##";
2801 }
2802
2803 str += mangle_name (src_name);
2804 str += ".gcov";
2805 }
2806
2807 return str;
2808}
2809
2810/* Mangle BASE name, copy it at the beginning of PTR buffer and
2811 return address of the \0 character of the buffer. */
2812
2813static char *
2814mangle_name (char const *base)
2815{
2816 /* Generate the source filename part. */
2817 if (!flag_preserve_paths)
2818 return xstrdup (lbasename (base));
2819 else
2820 return mangle_path (base);
2821}
2822
2823/* Scan through the bb_data for each line in the block, increment
2824 the line number execution count indicated by the execution count of
2825 the appropriate basic block. */
2826
2827static void
2828add_line_counts (coverage_info *coverage, function_info *fn)
2829{
2830 bool has_any_line = false;
2831 /* Scan each basic block. */
2832 for (unsigned ix = 0; ix != fn->blocks.size (); ix++)
2833 {
2834 line_info *line = NULL;
2835 block_info *block = &fn->blocks[ix];
2836 if (block->count && ix && ix + 1 != fn->blocks.size ())
2837 fn->blocks_executed++;
2838 for (unsigned i = 0; i < block->locations.size (); i++)
2839 {
2840 unsigned src_idx = block->locations[i].source_file_idx;
2841 vector<unsigned> &lines = block->locations[i].lines;
2842
2843 block->cycle.arc = NULL;
2844 block->cycle.ident = ~0U;
2845
2846 for (unsigned j = 0; j < lines.size (); j++)
2847 {
2848 unsigned ln = lines[j];
2849
2850 /* Line belongs to a function that is in a group. */
2851 if (fn->group_line_p (n: ln, src_idx))
2852 {
2853 gcc_assert (lines[j] - fn->start_line < fn->lines.size ());
2854 line = &(fn->lines[lines[j] - fn->start_line]);
2855 if (coverage)
2856 {
2857 if (!line->exists)
2858 coverage->lines++;
2859 if (!line->count && block->count)
2860 coverage->lines_executed++;
2861 }
2862 line->exists = 1;
2863 if (!block->exceptional)
2864 {
2865 line->unexceptional = 1;
2866 if (block->count == 0)
2867 line->has_unexecuted_block = 1;
2868 }
2869 line->count += block->count;
2870 }
2871 else
2872 {
2873 gcc_assert (ln < sources[src_idx].lines.size ());
2874 line = &(sources[src_idx].lines[ln]);
2875 if (coverage)
2876 {
2877 if (!line->exists)
2878 coverage->lines++;
2879 if (!line->count && block->count)
2880 coverage->lines_executed++;
2881 }
2882 line->exists = 1;
2883 if (!block->exceptional)
2884 {
2885 line->unexceptional = 1;
2886 if (block->count == 0)
2887 line->has_unexecuted_block = 1;
2888 }
2889 line->count += block->count;
2890 }
2891 }
2892
2893 has_any_line = true;
2894
2895 if (!ix || ix + 1 == fn->blocks.size ())
2896 /* Entry or exit block. */;
2897 else if (line != NULL)
2898 {
2899 line->blocks.push_back (x: block);
2900
2901 if (flag_branches)
2902 {
2903 arc_info *arc;
2904
2905 for (arc = block->succ; arc; arc = arc->succ_next)
2906 line->branches.push_back (x: arc);
2907 }
2908 }
2909 }
2910 }
2911
2912 if (!has_any_line)
2913 fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name,
2914 fn->get_name ());
2915}
2916
2917/* Accumulate info for LINE that belongs to SRC source file. If ADD_COVERAGE
2918 is set to true, update source file summary. */
2919
2920static void accumulate_line_info (line_info *line, source_info *src,
2921 bool add_coverage)
2922{
2923 if (add_coverage)
2924 for (vector<arc_info *>::iterator it = line->branches.begin ();
2925 it != line->branches.end (); it++)
2926 add_branch_counts (coverage: &src->coverage, arc: *it);
2927
2928 if (add_coverage)
2929 for (vector<block_info *>::iterator it = line->blocks.begin ();
2930 it != line->blocks.end (); it++)
2931 add_condition_counts (coverage: &src->coverage, block: *it);
2932
2933
2934 if (!line->blocks.empty ())
2935 {
2936 /* The user expects the line count to be the number of times
2937 a line has been executed. Simply summing the block count
2938 will give an artificially high number. The Right Thing
2939 is to sum the entry counts to the graph of blocks on this
2940 line, then find the elementary cycles of the local graph
2941 and add the transition counts of those cycles. */
2942 gcov_type count = 0;
2943
2944 /* Cycle detection. */
2945 for (vector<block_info *>::iterator it = line->blocks.begin ();
2946 it != line->blocks.end (); it++)
2947 {
2948 for (arc_info *arc = (*it)->pred; arc; arc = arc->pred_next)
2949 if (!line->has_block (needle: arc->src))
2950 count += arc->count;
2951 for (arc_info *arc = (*it)->succ; arc; arc = arc->succ_next)
2952 arc->cs_count = arc->count;
2953 }
2954
2955 /* Now, add the count of loops entirely on this line. */
2956 count += get_cycles_count (linfo&: *line);
2957 line->count = count;
2958
2959 if (line->count > src->maximum_count)
2960 src->maximum_count = line->count;
2961 }
2962
2963 if (line->exists && add_coverage)
2964 {
2965 src->coverage.lines++;
2966 if (line->count)
2967 src->coverage.lines_executed++;
2968 }
2969}
2970
2971/* Accumulate the line counts of a file. */
2972
2973static void
2974accumulate_line_counts (source_info *src)
2975{
2976 /* First work on group functions. */
2977 for (vector<function_info *>::iterator it = src->functions.begin ();
2978 it != src->functions.end (); it++)
2979 {
2980 function_info *fn = *it;
2981
2982 if (fn->src != src->index || !fn->is_group)
2983 continue;
2984
2985 for (vector<line_info>::iterator it2 = fn->lines.begin ();
2986 it2 != fn->lines.end (); it2++)
2987 {
2988 line_info *line = &(*it2);
2989 accumulate_line_info (line, src, add_coverage: true);
2990 }
2991 }
2992
2993 /* Work on global lines that line in source file SRC. */
2994 for (vector<line_info>::iterator it = src->lines.begin ();
2995 it != src->lines.end (); it++)
2996 accumulate_line_info (line: &(*it), src, add_coverage: true);
2997
2998 /* If not using intermediate mode, sum lines of group functions and
2999 add them to lines that live in a source file. */
3000 if (!flag_json_format)
3001 for (vector<function_info *>::iterator it = src->functions.begin ();
3002 it != src->functions.end (); it++)
3003 {
3004 function_info *fn = *it;
3005
3006 if (fn->src != src->index || !fn->is_group)
3007 continue;
3008
3009 for (unsigned i = 0; i < fn->lines.size (); i++)
3010 {
3011 line_info *fn_line = &fn->lines[i];
3012 if (fn_line->exists)
3013 {
3014 unsigned ln = fn->start_line + i;
3015 line_info *src_line = &src->lines[ln];
3016
3017 if (!src_line->exists)
3018 src->coverage.lines++;
3019 if (!src_line->count && fn_line->count)
3020 src->coverage.lines_executed++;
3021
3022 src_line->count += fn_line->count;
3023 src_line->exists = 1;
3024
3025 if (fn_line->has_unexecuted_block)
3026 src_line->has_unexecuted_block = 1;
3027
3028 if (fn_line->unexceptional)
3029 src_line->unexceptional = 1;
3030 }
3031 }
3032 }
3033}
3034
3035/* Output information about the conditions in block BINFO. The output includes
3036 * a summary (n/m outcomes covered) and a list of the missing (uncovered)
3037 * outcomes. */
3038
3039static void
3040output_conditions (FILE *gcov_file, const block_info *binfo)
3041{
3042 const condition_info& info = binfo->conditions;
3043 if (info.n_terms == 0)
3044 return;
3045
3046 const int expected = 2 * info.n_terms;
3047 const int got = info.popcount ();
3048
3049 fnotice (gcov_file, "condition outcomes covered %d/%d\n", got, expected);
3050 if (expected == got)
3051 return;
3052
3053 for (unsigned i = 0; i < info.n_terms; i++)
3054 {
3055 gcov_type_unsigned index = 1;
3056 index <<= i;
3057 if ((index & info.truev & info.falsev))
3058 continue;
3059
3060 const char *t = (index & info.truev) ? "" : "true";
3061 const char *f = (index & info.falsev) ? "" : " false";
3062 fnotice (gcov_file, "condition %2u not covered (%s%s)\n", i, t, f + !t[0]);
3063 }
3064}
3065
3066/* Output information about ARC number IX. Returns nonzero if
3067 anything is output. */
3068
3069static int
3070output_branch_count (FILE *gcov_file, int ix, const arc_info *arc)
3071{
3072 if (arc->is_call_non_return)
3073 {
3074 if (arc->src->count)
3075 {
3076 fnotice (gcov_file, "call %2d returned %s\n", ix,
3077 format_gcov (top: arc->src->count - arc->count,
3078 bottom: arc->src->count, decimal_places: -flag_counts));
3079 }
3080 else
3081 fnotice (gcov_file, "call %2d never executed\n", ix);
3082 }
3083 else if (!arc->is_unconditional)
3084 {
3085 if (arc->src->count)
3086 fnotice (gcov_file, "branch %2d taken %s%s", ix,
3087 format_gcov (top: arc->count, bottom: arc->src->count, decimal_places: -flag_counts),
3088 arc->fall_through ? " (fallthrough)"
3089 : arc->is_throw ? " (throw)" : "");
3090 else
3091 fnotice (gcov_file, "branch %2d never executed%s", ix,
3092 (arc->fall_through ? " (fallthrough)"
3093 : arc->is_throw ? " (throw)" : ""));
3094
3095 if (flag_verbose)
3096 fnotice (gcov_file, " (BB %d)", arc->dst->id);
3097
3098 fnotice (gcov_file, "\n");
3099 }
3100 else if (flag_unconditional && !arc->dst->is_call_return)
3101 {
3102 if (arc->src->count)
3103 fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
3104 format_gcov (top: arc->count, bottom: arc->src->count, decimal_places: -flag_counts));
3105 else
3106 fnotice (gcov_file, "unconditional %2d never executed\n", ix);
3107 }
3108 else
3109 return 0;
3110 return 1;
3111}
3112
3113static const char *
3114read_line (FILE *file)
3115{
3116 static char *string;
3117 static size_t string_len;
3118 size_t pos = 0;
3119
3120 if (!string_len)
3121 {
3122 string_len = 200;
3123 string = XNEWVEC (char, string_len);
3124 }
3125
3126 while (fgets (s: string + pos, n: string_len - pos, stream: file))
3127 {
3128 size_t len = strlen (s: string + pos);
3129
3130 if (len && string[pos + len - 1] == '\n')
3131 {
3132 string[pos + len - 1] = 0;
3133 return string;
3134 }
3135 pos += len;
3136 /* If the file contains NUL characters or an incomplete
3137 last line, which can happen more than once in one run,
3138 we have to avoid doubling the STRING_LEN unnecessarily. */
3139 if (pos > string_len / 2)
3140 {
3141 string_len *= 2;
3142 string = XRESIZEVEC (char, string, string_len);
3143 }
3144 }
3145
3146 return pos ? string : NULL;
3147}
3148
3149/* Pad string S with spaces from left to have total width equal to 9. */
3150
3151static void
3152pad_count_string (string &s)
3153{
3154 if (s.size () < 9)
3155 s.insert (pos: 0, n: 9 - s.size (), c: ' ');
3156}
3157
3158/* Print GCOV line beginning to F stream. If EXISTS is set to true, the
3159 line exists in source file. UNEXCEPTIONAL indicated that it's not in
3160 an exceptional statement. The output is printed for LINE_NUM of given
3161 COUNT of executions. EXCEPTIONAL_STRING and UNEXCEPTIONAL_STRING are
3162 used to indicate non-executed blocks. */
3163
3164static void
3165output_line_beginning (FILE *f, bool exists, bool unexceptional,
3166 bool has_unexecuted_block,
3167 gcov_type count, unsigned line_num,
3168 const char *exceptional_string,
3169 const char *unexceptional_string,
3170 unsigned int maximum_count)
3171{
3172 string s;
3173 if (exists)
3174 {
3175 if (count > 0)
3176 {
3177 s = format_gcov (top: count, bottom: 0, decimal_places: -1);
3178 if (has_unexecuted_block
3179 && bbg_supports_has_unexecuted_blocks)
3180 {
3181 if (flag_use_colors)
3182 {
3183 pad_count_string (s);
3184 s.insert (pos: 0, SGR_SEQ (COLOR_BG_MAGENTA
3185 COLOR_SEPARATOR COLOR_FG_WHITE));
3186 s += SGR_RESET;
3187 }
3188 else
3189 s += "*";
3190 }
3191 pad_count_string (s);
3192 }
3193 else
3194 {
3195 if (flag_use_colors)
3196 {
3197 s = "0";
3198 pad_count_string (s);
3199 if (unexceptional)
3200 s.insert (pos: 0, SGR_SEQ (COLOR_BG_RED
3201 COLOR_SEPARATOR COLOR_FG_WHITE));
3202 else
3203 s.insert (pos: 0, SGR_SEQ (COLOR_BG_CYAN
3204 COLOR_SEPARATOR COLOR_FG_WHITE));
3205 s += SGR_RESET;
3206 }
3207 else
3208 {
3209 s = unexceptional ? unexceptional_string : exceptional_string;
3210 pad_count_string (s);
3211 }
3212 }
3213 }
3214 else
3215 {
3216 s = "-";
3217 pad_count_string (s);
3218 }
3219
3220 /* Format line number in output. */
3221 char buffer[16];
3222 sprintf (s: buffer, format: "%5u", line_num);
3223 string linestr (buffer);
3224
3225 if (flag_use_hotness_colors && maximum_count)
3226 {
3227 if (count * 2 > maximum_count) /* > 50%. */
3228 linestr.insert (pos: 0, SGR_SEQ (COLOR_BG_RED));
3229 else if (count * 5 > maximum_count) /* > 20%. */
3230 linestr.insert (pos: 0, SGR_SEQ (COLOR_BG_YELLOW));
3231 else if (count * 10 > maximum_count) /* > 10%. */
3232 linestr.insert (pos: 0, SGR_SEQ (COLOR_BG_GREEN));
3233 linestr += SGR_RESET;
3234 }
3235
3236 fprintf (stream: f, format: "%s:%s", s.c_str (), linestr.c_str ());
3237}
3238
3239static void
3240print_source_line (FILE *f, const vector<const char *> &source_lines,
3241 unsigned line)
3242{
3243 gcc_assert (line >= 1);
3244 gcc_assert (line <= source_lines.size ());
3245
3246 fprintf (stream: f, format: ":%s\n", source_lines[line - 1]);
3247}
3248
3249/* Output line details for LINE and print it to F file. LINE lives on
3250 LINE_NUM. */
3251
3252static void
3253output_line_details (FILE *f, const line_info *line, unsigned line_num)
3254{
3255 if (flag_all_blocks)
3256 {
3257 arc_info *arc;
3258 int jx = 0;
3259 for (vector<block_info *>::const_iterator it = line->blocks.begin ();
3260 it != line->blocks.end (); it++)
3261 {
3262 if (!(*it)->is_call_return)
3263 {
3264 output_line_beginning (f, exists: line->exists,
3265 unexceptional: (*it)->exceptional, has_unexecuted_block: false,
3266 count: (*it)->count, line_num,
3267 exceptional_string: "%%%%%", unexceptional_string: "$$$$$", maximum_count: 0);
3268 fprintf (stream: f, format: "-block %d", (*it)->id);
3269 if (flag_verbose)
3270 fprintf (stream: f, format: " (BB %u)", (*it)->id);
3271 fprintf (stream: f, format: "\n");
3272 }
3273 if (flag_branches)
3274 for (arc = (*it)->succ; arc; arc = arc->succ_next)
3275 jx += output_branch_count (gcov_file: f, ix: jx, arc);
3276
3277 if (flag_conditions)
3278 output_conditions (gcov_file: f, binfo: *it);
3279 }
3280 }
3281 else
3282 {
3283 if (flag_branches)
3284 {
3285 int ix;
3286
3287 ix = 0;
3288 for (vector<arc_info *>::const_iterator it = line->branches.begin ();
3289 it != line->branches.end (); it++)
3290 ix += output_branch_count (gcov_file: f, ix, arc: (*it));
3291 }
3292
3293 if (flag_conditions)
3294 {
3295 for (vector<block_info *>::const_iterator it = line->blocks.begin ();
3296 it != line->blocks.end (); it++)
3297 output_conditions (gcov_file: f, binfo: *it);
3298 }
3299 }
3300}
3301
3302/* Output detail statistics about function FN to file F. */
3303
3304static void
3305output_function_details (FILE *f, function_info *fn)
3306{
3307 if (!flag_branches)
3308 return;
3309
3310 arc_info *arc = fn->blocks[EXIT_BLOCK].pred;
3311 gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
3312 gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
3313
3314 for (; arc; arc = arc->pred_next)
3315 if (arc->fake)
3316 return_count -= arc->count;
3317
3318 fprintf (stream: f, format: "function %s", fn->get_name ());
3319 fprintf (stream: f, format: " called %s",
3320 format_gcov (top: called_count, bottom: 0, decimal_places: -1));
3321 fprintf (stream: f, format: " returned %s",
3322 format_gcov (top: return_count, bottom: called_count, decimal_places: 0));
3323 fprintf (stream: f, format: " blocks executed %s",
3324 format_gcov (top: fn->blocks_executed, bottom: fn->get_block_count (), decimal_places: 0));
3325 fprintf (stream: f, format: "\n");
3326}
3327
3328/* Read in the source file one line at a time, and output that line to
3329 the gcov file preceded by its execution count and other
3330 information. */
3331
3332static void
3333output_lines (FILE *gcov_file, const source_info *src)
3334{
3335#define DEFAULT_LINE_START " -: 0:"
3336#define FN_SEPARATOR "------------------\n"
3337
3338 FILE *source_file;
3339 const char *retval;
3340
3341 /* Print colorization legend. */
3342 if (flag_use_colors)
3343 fprintf (stream: gcov_file, format: "%s",
3344 DEFAULT_LINE_START "Colorization: profile count: " \
3345 SGR_SEQ (COLOR_BG_CYAN) "zero coverage (exceptional)" SGR_RESET \
3346 " " \
3347 SGR_SEQ (COLOR_BG_RED) "zero coverage (unexceptional)" SGR_RESET \
3348 " " \
3349 SGR_SEQ (COLOR_BG_MAGENTA) "unexecuted block" SGR_RESET "\n");
3350
3351 if (flag_use_hotness_colors)
3352 fprintf (stream: gcov_file, format: "%s",
3353 DEFAULT_LINE_START "Colorization: line numbers: hotness: " \
3354 SGR_SEQ (COLOR_BG_RED) "> 50%" SGR_RESET " " \
3355 SGR_SEQ (COLOR_BG_YELLOW) "> 20%" SGR_RESET " " \
3356 SGR_SEQ (COLOR_BG_GREEN) "> 10%" SGR_RESET "\n");
3357
3358 fprintf (stream: gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
3359 if (!multiple_files)
3360 {
3361 fprintf (stream: gcov_file, DEFAULT_LINE_START "Graph:%s\n", bbg_file_name);
3362 fprintf (stream: gcov_file, DEFAULT_LINE_START "Data:%s\n",
3363 no_data_file ? "-" : da_file_name);
3364 fprintf (stream: gcov_file, DEFAULT_LINE_START "Runs:%u\n", object_runs);
3365 }
3366
3367 source_file = fopen (filename: src->name, modes: "r");
3368 if (!source_file)
3369 fnotice (stderr, "Cannot open source file %s\n", src->name);
3370 else if (src->file_time == 0)
3371 fprintf (stream: gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
3372
3373 vector<const char *> source_lines;
3374 if (source_file)
3375 while ((retval = read_line (file: source_file)) != NULL)
3376 source_lines.push_back (x: xstrdup (retval));
3377
3378 unsigned line_start_group = 0;
3379 vector<function_info *> *fns;
3380
3381 for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
3382 {
3383 if (line_num >= src->lines.size ())
3384 {
3385 fprintf (stream: gcov_file, format: "%9s:%5u", "-", line_num);
3386 print_source_line (f: gcov_file, source_lines, line: line_num);
3387 continue;
3388 }
3389
3390 const line_info *line = &src->lines[line_num];
3391
3392 if (line_start_group == 0)
3393 {
3394 fns = src->get_functions_at_location (line_num);
3395 if (fns != NULL && fns->size () > 1)
3396 {
3397 /* It's possible to have functions that partially overlap,
3398 thus take the maximum end_line of functions starting
3399 at LINE_NUM. */
3400 for (unsigned i = 0; i < fns->size (); i++)
3401 if ((*fns)[i]->end_line > line_start_group)
3402 line_start_group = (*fns)[i]->end_line;
3403 }
3404 else if (fns != NULL && fns->size () == 1)
3405 {
3406 function_info *fn = (*fns)[0];
3407 output_function_details (f: gcov_file, fn);
3408 }
3409 }
3410
3411 /* For lines which don't exist in the .bb file, print '-' before
3412 the source line. For lines which exist but were never
3413 executed, print '#####' or '=====' before the source line.
3414 Otherwise, print the execution count before the source line.
3415 There are 16 spaces of indentation added before the source
3416 line so that tabs won't be messed up. */
3417 output_line_beginning (f: gcov_file, exists: line->exists, unexceptional: line->unexceptional,
3418 has_unexecuted_block: line->has_unexecuted_block, count: line->count,
3419 line_num, exceptional_string: "=====", unexceptional_string: "#####", maximum_count: src->maximum_count);
3420
3421 print_source_line (f: gcov_file, source_lines, line: line_num);
3422 output_line_details (f: gcov_file, line, line_num);
3423
3424 if (line_start_group == line_num)
3425 {
3426 for (vector<function_info *>::iterator it = fns->begin ();
3427 it != fns->end (); it++)
3428 {
3429 function_info *fn = *it;
3430 vector<line_info> &lines = fn->lines;
3431
3432 fprintf (stream: gcov_file, FN_SEPARATOR);
3433
3434 string fn_name = fn->get_name ();
3435 if (flag_use_colors)
3436 {
3437 fn_name.insert (pos: 0, SGR_SEQ (COLOR_FG_CYAN));
3438 fn_name += SGR_RESET;
3439 }
3440
3441 fprintf (stream: gcov_file, format: "%s:\n", fn_name.c_str ());
3442
3443 output_function_details (f: gcov_file, fn);
3444
3445 /* Print all lines covered by the function. */
3446 for (unsigned i = 0; i < lines.size (); i++)
3447 {
3448 line_info *line = &lines[i];
3449 unsigned l = fn->start_line + i;
3450
3451 /* For lines which don't exist in the .bb file, print '-'
3452 before the source line. For lines which exist but
3453 were never executed, print '#####' or '=====' before
3454 the source line. Otherwise, print the execution count
3455 before the source line.
3456 There are 16 spaces of indentation added before the source
3457 line so that tabs won't be messed up. */
3458 output_line_beginning (f: gcov_file, exists: line->exists,
3459 unexceptional: line->unexceptional,
3460 has_unexecuted_block: line->has_unexecuted_block,
3461 count: line->count,
3462 line_num: l, exceptional_string: "=====", unexceptional_string: "#####",
3463 maximum_count: src->maximum_count);
3464
3465 print_source_line (f: gcov_file, source_lines, line: l);
3466 output_line_details (f: gcov_file, line, line_num: l);
3467 }
3468 }
3469
3470 fprintf (stream: gcov_file, FN_SEPARATOR);
3471 line_start_group = 0;
3472 }
3473 }
3474
3475 if (source_file)
3476 fclose (stream: source_file);
3477}
3478

source code of gcc/gcov.cc