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