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