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