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