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