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