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

source code of gcc/gcov.cc