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

source code of gcc/gcov.cc