diff options
author | Sam Thursfield <sam.thursfield@codethink.co.uk> | 2017-11-13 16:28:05 +0000 |
---|---|---|
committer | Sam Thursfield <sam.thursfield@codethink.co.uk> | 2017-11-13 16:29:09 +0000 |
commit | 03ac50856c9fc8c96b7a17239ee40a10397750a7 (patch) | |
tree | a648c6d3428e4757e003f6ed1748adb9613065db /gcc/gcov.c | |
parent | 34efdaf078b01a7387007c4e6bde6db86384c4b7 (diff) | |
download | gcc-tarball-03ac50856c9fc8c96b7a17239ee40a10397750a7.tar.gz |
gcc 7.2.0
This is imported manually due to a bug in the tarball import script.
See the baserock-dev mailing list archives (November 2017) for a
more detailed explaination of the issue.
Diffstat (limited to 'gcc/gcov.c')
-rw-r--r-- | gcc/gcov.c | 2600 |
1 files changed, 0 insertions, 2600 deletions
diff --git a/gcc/gcov.c b/gcc/gcov.c deleted file mode 100644 index bb26a1a978..0000000000 --- a/gcc/gcov.c +++ /dev/null @@ -1,2600 +0,0 @@ -/* Gcov.c: prepend line execution counts and branch probabilities to a - source file. - Copyright (C) 1990-2017 Free Software Foundation, Inc. - Contributed by James E. Wilson of Cygnus Support. - Mangled by Bob Manson of Cygnus Support. - Mangled further by Nathan Sidwell <nathan@codesourcery.com> - -Gcov is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -Gcov is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Gcov; see the file COPYING3. If not see -<http://www.gnu.org/licenses/>. */ - -/* ??? Print a list of the ten blocks with the highest execution counts, - and list the line numbers corresponding to those blocks. Also, perhaps - list the line numbers with the highest execution counts, only printing - the first if there are several which are all listed in the same block. */ - -/* ??? Should have an option to print the number of basic blocks, and the - percent of them that are covered. */ - -/* Need an option to show individual block counts, and show - probabilities of fall through arcs. */ - -#include "config.h" -#define INCLUDE_ALGORITHM -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "intl.h" -#include "diagnostic.h" -#include "version.h" -#include "demangle.h" - -#include <getopt.h> - -#include "md5.h" - -using namespace std; - -#define IN_GCOV 1 -#include "gcov-io.h" -#include "gcov-io.c" - -/* The gcno file is generated by -ftest-coverage option. The gcda file is - generated by a program compiled with -fprofile-arcs. Their formats - are documented in gcov-io.h. */ - -/* The functions in this file for creating and solution program flow graphs - are very similar to functions in the gcc source file profile.c. In - some places we make use of the knowledge of how profile.c works to - select particular algorithms here. */ - -/* The code validates that the profile information read in corresponds - to the code currently being compiled. Rather than checking for - identical files, the code below compares a checksum on the CFG - (based on the order of basic blocks and the arcs in the CFG). If - the CFG checksum in the gcda file match the CFG checksum in the - gcno file, the profile data will be used. */ - -/* This is the size of the buffer used to read in source file lines. */ - -struct function_info; -struct block_info; -struct source_info; - -/* Describes an arc between two basic blocks. */ - -typedef struct arc_info -{ - /* source and destination blocks. */ - struct block_info *src; - struct block_info *dst; - - /* transition counts. */ - gcov_type count; - /* used in cycle search, so that we do not clobber original counts. */ - gcov_type cs_count; - - unsigned int count_valid : 1; - unsigned int on_tree : 1; - unsigned int fake : 1; - unsigned int fall_through : 1; - - /* Arc to a catch handler. */ - unsigned int is_throw : 1; - - /* Arc is for a function that abnormally returns. */ - unsigned int is_call_non_return : 1; - - /* Arc is for catch/setjmp. */ - unsigned int is_nonlocal_return : 1; - - /* Is an unconditional branch. */ - unsigned int is_unconditional : 1; - - /* Loop making arc. */ - unsigned int cycle : 1; - - /* Next branch on line. */ - struct arc_info *line_next; - - /* Links to next arc on src and dst lists. */ - struct arc_info *succ_next; - struct arc_info *pred_next; -} arc_t; - -/* Describes a basic block. Contains lists of arcs to successor and - predecessor blocks. */ - -typedef struct block_info -{ - /* Chain of exit and entry arcs. */ - arc_t *succ; - arc_t *pred; - - /* Number of unprocessed exit and entry arcs. */ - gcov_type num_succ; - gcov_type num_pred; - - /* Block execution count. */ - gcov_type count; - unsigned flags : 12; - unsigned count_valid : 1; - unsigned valid_chain : 1; - unsigned invalid_chain : 1; - unsigned exceptional : 1; - - /* Block is a call instrumenting site. */ - unsigned is_call_site : 1; /* Does the call. */ - unsigned is_call_return : 1; /* Is the return. */ - - /* Block is a landing pad for longjmp or throw. */ - unsigned is_nonlocal_return : 1; - - union - { - struct - { - /* Array of line numbers and source files. source files are - introduced by a linenumber of zero, the next 'line number' is - the number of the source file. Always starts with a source - file. */ - unsigned *encoding; - unsigned num; - } line; /* Valid until blocks are linked onto lines */ - struct - { - /* Single line graph cycle workspace. Used for all-blocks - mode. */ - arc_t *arc; - unsigned ident; - } cycle; /* Used in all-blocks mode, after blocks are linked onto - lines. */ - } u; - - /* Temporary chain for solving graph, and for chaining blocks on one - line. */ - struct block_info *chain; - -} block_t; - -/* Describes a single function. Contains an array of basic blocks. */ - -typedef struct function_info -{ - /* Name of function. */ - char *name; - char *demangled_name; - unsigned ident; - unsigned lineno_checksum; - unsigned cfg_checksum; - - /* The graph contains at least one fake incoming edge. */ - unsigned has_catch : 1; - - /* Array of basic blocks. Like in GCC, the entry block is - at blocks[0] and the exit block is at blocks[1]. */ -#define ENTRY_BLOCK (0) -#define EXIT_BLOCK (1) - block_t *blocks; - unsigned num_blocks; - unsigned blocks_executed; - - /* Raw arc coverage counts. */ - gcov_type *counts; - unsigned num_counts; - - /* First line number & file. */ - unsigned line; - unsigned src; - - /* Next function in same source file. */ - struct function_info *next_file_fn; - - /* Next function. */ - struct function_info *next; -} function_t; - -/* Describes coverage of a file or function. */ - -typedef struct coverage_info -{ - int lines; - int lines_executed; - - int branches; - int branches_executed; - int branches_taken; - - int calls; - int calls_executed; - - char *name; -} coverage_t; - -/* Describes a single line of source. Contains a chain of basic blocks - with code on it. */ - -typedef struct line_info -{ - /* Return true when NEEDLE is one of basic blocks the line belongs to. */ - bool has_block (block_t *needle); - - gcov_type count; /* execution count */ - union - { - arc_t *branches; /* branches from blocks that end on this - line. Used for branch-counts when not - all-blocks mode. */ - block_t *blocks; /* blocks which start on this line. Used - in all-blocks mode. */ - } u; - unsigned exists : 1; - unsigned unexceptional : 1; -} line_t; - -bool -line_t::has_block (block_t *needle) -{ - for (block_t *n = u.blocks; n; n = n->chain) - if (n == needle) - return true; - - return false; -} - -/* Describes a file mentioned in the block graph. Contains an array - of line info. */ - -typedef struct source_info -{ - /* Canonical name of source file. */ - char *name; - time_t file_time; - - /* Array of line information. */ - line_t *lines; - unsigned num_lines; - - coverage_t coverage; - - /* Functions in this source file. These are in ascending line - number order. */ - function_t *functions; -} source_t; - -typedef struct name_map -{ - char *name; /* Source file name */ - unsigned src; /* Source file */ -} name_map_t; - -/* Holds a list of function basic block graphs. */ - -static function_t *functions; -static function_t **fn_end = &functions; - -static source_t *sources; /* Array of source files */ -static unsigned n_sources; /* Number of sources */ -static unsigned a_sources; /* Allocated sources */ - -static name_map_t *names; /* Mapping of file names to sources */ -static unsigned n_names; /* Number of names */ -static unsigned a_names; /* Allocated names */ - -/* This holds data summary information. */ - -static unsigned object_runs; -static unsigned program_count; - -static unsigned total_lines; -static unsigned total_executed; - -/* Modification time of graph file. */ - -static time_t bbg_file_time; - -/* Name of the notes (gcno) output file. The "bbg" prefix is for - historical reasons, when the notes file contained only the - basic block graph notes. */ - -static char *bbg_file_name; - -/* Stamp of the bbg file */ -static unsigned bbg_stamp; - -/* Name and file pointer of the input file for the count data (gcda). */ - -static char *da_file_name; - -/* Data file is missing. */ - -static int no_data_file; - -/* If there is several input files, compute and display results after - reading all data files. This way if two or more gcda file refer to - the same source file (eg inline subprograms in a .h file), the - counts are added. */ - -static int multiple_files = 0; - -/* Output branch probabilities. */ - -static int flag_branches = 0; - -/* Show unconditional branches too. */ -static int flag_unconditional = 0; - -/* Output a gcov file if this is true. This is on by default, and can - be turned off by the -n option. */ - -static int flag_gcov_file = 1; - -/* Output progress indication if this is true. This is off by default - and can be turned on by the -d option. */ - -static int flag_display_progress = 0; - -/* Output *.gcov file in intermediate format used by 'lcov'. */ - -static int flag_intermediate_format = 0; - -/* Output demangled function names. */ - -static int flag_demangled_names = 0; - -/* For included files, make the gcov output file name include the name - of the input source file. For example, if x.h is included in a.c, - then the output file name is a.c##x.h.gcov instead of x.h.gcov. */ - -static int flag_long_names = 0; - -/* For situations when a long name can potentially hit filesystem path limit, - let's calculate md5sum of the path and append it to a file name. */ - -static int flag_hash_filenames = 0; - -/* Output count information for every basic block, not merely those - that contain line number information. */ - -static int flag_all_blocks = 0; - -/* Output summary info for each function. */ - -static int flag_function_summary = 0; - -/* Object directory file prefix. This is the directory/file where the - graph and data files are looked for, if nonzero. */ - -static char *object_directory = 0; - -/* Source directory prefix. This is removed from source pathnames - that match, when generating the output file name. */ - -static char *source_prefix = 0; -static size_t source_length = 0; - -/* Only show data for sources with relative pathnames. Absolute ones - usually indicate a system header file, which although it may - contain inline functions, is usually uninteresting. */ -static int flag_relative_only = 0; - -/* Preserve all pathname components. Needed when object files and - source files are in subdirectories. '/' is mangled as '#', '.' is - elided and '..' mangled to '^'. */ - -static int flag_preserve_paths = 0; - -/* Output the number of times a branch was taken as opposed to the percentage - of times it was taken. */ - -static int flag_counts = 0; - -/* Forward declarations. */ -static int process_args (int, char **); -static void print_usage (int) ATTRIBUTE_NORETURN; -static void print_version (void) ATTRIBUTE_NORETURN; -static void process_file (const char *); -static void generate_results (const char *); -static void create_file_names (const char *); -static int name_search (const void *, const void *); -static int name_sort (const void *, const void *); -static char *canonicalize_name (const char *); -static unsigned find_source (const char *); -static function_t *read_graph_file (void); -static int read_count_file (function_t *); -static void solve_flow_graph (function_t *); -static void find_exception_blocks (function_t *); -static void add_branch_counts (coverage_t *, const arc_t *); -static void add_line_counts (coverage_t *, function_t *); -static void executed_summary (unsigned, unsigned); -static void function_summary (const coverage_t *, const char *); -static const char *format_gcov (gcov_type, gcov_type, int); -static void accumulate_line_counts (source_t *); -static void output_gcov_file (const char *, source_t *); -static int output_branch_count (FILE *, int, const arc_t *); -static void output_lines (FILE *, const source_t *); -static char *make_gcov_file_name (const char *, const char *); -static char *mangle_name (const char *, char *); -static void release_structures (void); -static void release_function (function_t *); -extern int main (int, char **); - -/* Cycle detection! - There are a bajillion algorithms that do this. Boost's function is named - hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in - "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs" - (url at <http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf>). - - The basic algorithm is simple: effectively, we're finding all simple paths - in a subgraph (that shrinks every iteration). Duplicates are filtered by - "blocking" a path when a node is added to the path (this also prevents non- - simple paths)--the node is unblocked only when it participates in a cycle. - */ - -typedef vector<arc_t *> arc_vector_t; -typedef vector<const block_t *> block_vector_t; - -/* Enum with types of loop in CFG. */ - -enum loop_type -{ - NO_LOOP = 0, - LOOP = 1, - NEGATIVE_LOOP = 3 -}; - -/* Loop_type operator that merges two values: A and B. */ - -inline loop_type& operator |= (loop_type& a, loop_type b) -{ - return a = static_cast<loop_type> (a | b); -} - -/* Handle cycle identified by EDGES, where the function finds minimum cs_count - and subtract the value from all counts. The subtracted value is added - to COUNT. Returns type of loop. */ - -static loop_type -handle_cycle (const arc_vector_t &edges, int64_t &count) -{ - /* Find the minimum edge of the cycle, and reduce all nodes in the cycle by - that amount. */ - int64_t cycle_count = INTTYPE_MAXIMUM (int64_t); - for (unsigned i = 0; i < edges.size (); i++) - { - int64_t ecount = edges[i]->cs_count; - if (cycle_count > ecount) - cycle_count = ecount; - } - count += cycle_count; - for (unsigned i = 0; i < edges.size (); i++) - edges[i]->cs_count -= cycle_count; - - return cycle_count < 0 ? NEGATIVE_LOOP : LOOP; -} - -/* Unblock a block U from BLOCKED. Apart from that, iterate all blocks - blocked by U in BLOCK_LISTS. */ - -static void -unblock (const block_t *u, block_vector_t &blocked, - vector<block_vector_t > &block_lists) -{ - block_vector_t::iterator it = find (blocked.begin (), blocked.end (), u); - if (it == blocked.end ()) - return; - - unsigned index = it - blocked.begin (); - blocked.erase (it); - - for (block_vector_t::iterator it2 = block_lists[index].begin (); - it2 != block_lists[index].end (); it2++) - unblock (*it2, blocked, block_lists); - for (unsigned j = 0; j < block_lists[index].size (); j++) - unblock (u, blocked, block_lists); - - block_lists.erase (block_lists.begin () + index); -} - -/* Find circuit going to block V, PATH is provisional seen cycle. - BLOCKED is vector of blocked vertices, BLOCK_LISTS contains vertices - blocked by a block. COUNT is accumulated count of the current LINE. - Returns what type of loop it contains. */ - -static loop_type -circuit (block_t *v, arc_vector_t &path, block_t *start, - block_vector_t &blocked, vector<block_vector_t> &block_lists, - line_t &linfo, int64_t &count) -{ - loop_type result = NO_LOOP; - - /* Add v to the block list. */ - gcc_assert (find (blocked.begin (), blocked.end (), v) == blocked.end ()); - blocked.push_back (v); - block_lists.push_back (block_vector_t ()); - - for (arc_t *arc = v->succ; arc; arc = arc->succ_next) - { - block_t *w = arc->dst; - if (w < start || !linfo.has_block (w)) - continue; - - path.push_back (arc); - if (w == start) - /* Cycle has been found. */ - result |= handle_cycle (path, count); - else if (find (blocked.begin (), blocked.end (), w) == blocked.end ()) - result |= circuit (w, path, start, blocked, block_lists, linfo, count); - - path.pop_back (); - } - - if (result != NO_LOOP) - unblock (v, blocked, block_lists); - else - for (arc_t *arc = v->succ; arc; arc = arc->succ_next) - { - block_t *w = arc->dst; - if (w < start || !linfo.has_block (w)) - continue; - - size_t index - = find (blocked.begin (), blocked.end (), w) - blocked.begin (); - gcc_assert (index < blocked.size ()); - block_vector_t &list = block_lists[index]; - if (find (list.begin (), list.end (), v) == list.end ()) - list.push_back (v); - } - - return result; -} - -/* Find cycles for a LINFO. If HANDLE_NEGATIVE_CYCLES is set and the line - contains a negative loop, then perform the same function once again. */ - -static gcov_type -get_cycles_count (line_t &linfo, bool handle_negative_cycles = true) -{ - /* Note that this algorithm works even if blocks aren't in sorted order. - Each iteration of the circuit detection is completely independent - (except for reducing counts, but that shouldn't matter anyways). - Therefore, operating on a permuted order (i.e., non-sorted) only - has the effect of permuting the output cycles. */ - - loop_type result = NO_LOOP; - gcov_type count = 0; - for (block_t *block = linfo.u.blocks; block; block = block->chain) - { - arc_vector_t path; - block_vector_t blocked; - vector<block_vector_t > block_lists; - result |= circuit (block, path, block, blocked, block_lists, linfo, - count); - } - - /* If we have a negative cycle, repeat the find_cycles routine. */ - if (result == NEGATIVE_LOOP && handle_negative_cycles) - count += get_cycles_count (linfo, false); - - return count; -} - -int -main (int argc, char **argv) -{ - int argno; - int first_arg; - const char *p; - - p = argv[0] + strlen (argv[0]); - while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) - --p; - progname = p; - - xmalloc_set_program_name (progname); - - /* Unlock the stdio streams. */ - unlock_std_streams (); - - gcc_init_libintl (); - - diagnostic_initialize (global_dc, 0); - - /* Handle response files. */ - expandargv (&argc, &argv); - - a_names = 10; - names = XNEWVEC (name_map_t, a_names); - a_sources = 10; - sources = XNEWVEC (source_t, a_sources); - - argno = process_args (argc, argv); - if (optind == argc) - print_usage (true); - - if (argc - argno > 1) - multiple_files = 1; - - first_arg = argno; - - for (; argno != argc; argno++) - { - if (flag_display_progress) - printf ("Processing file %d out of %d\n", argno - first_arg + 1, - argc - first_arg); - process_file (argv[argno]); - } - - generate_results (multiple_files ? NULL : argv[argc - 1]); - - release_structures (); - - return 0; -} - -/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, - otherwise the output of --help. */ - -static void -print_usage (int error_p) -{ - FILE *file = error_p ? stderr : stdout; - int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; - - fnotice (file, "Usage: gcov [OPTION...] SOURCE|OBJ...\n\n"); - fnotice (file, "Print code coverage information.\n\n"); - fnotice (file, " -h, --help Print this help, then exit\n"); - fnotice (file, " -a, --all-blocks Show information for every basic block\n"); - fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); - fnotice (file, " -c, --branch-counts Output counts of branches taken\n\ - rather than percentages\n"); - fnotice (file, " -d, --display-progress Display progress information\n"); - fnotice (file, " -f, --function-summaries Output summaries for each function\n"); - fnotice (file, " -i, --intermediate-format Output .gcov file in intermediate text format\n"); - fnotice (file, " -l, --long-file-names Use long output file names for included\n\ - source files\n"); - fnotice (file, " -m, --demangled-names Output demangled function names\n"); - fnotice (file, " -n, --no-output Do not create an output file\n"); - fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n"); - fnotice (file, " -p, --preserve-paths Preserve all pathname components\n"); - fnotice (file, " -r, --relative-only Only show data for relative sources\n"); - fnotice (file, " -s, --source-prefix DIR Source prefix to elide\n"); - fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n"); - fnotice (file, " -v, --version Print version number, then exit\n"); - fnotice (file, " -x, --hash-filenames Hash long pathnames\n"); - fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", - bug_report_url); - exit (status); -} - -/* Print version information and exit. */ - -static void -print_version (void) -{ - fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string); - fprintf (stdout, "Copyright %s 2017 Free Software Foundation, Inc.\n", - _("(C)")); - fnotice (stdout, - _("This is free software; see the source for copying conditions.\n" - "There is NO warranty; not even for MERCHANTABILITY or \n" - "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); - exit (SUCCESS_EXIT_CODE); -} - -static const struct option options[] = -{ - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "all-blocks", no_argument, NULL, 'a' }, - { "branch-probabilities", no_argument, NULL, 'b' }, - { "branch-counts", no_argument, NULL, 'c' }, - { "intermediate-format", no_argument, NULL, 'i' }, - { "no-output", no_argument, NULL, 'n' }, - { "long-file-names", no_argument, NULL, 'l' }, - { "function-summaries", no_argument, NULL, 'f' }, - { "demangled-names", no_argument, NULL, 'm' }, - { "preserve-paths", no_argument, NULL, 'p' }, - { "relative-only", no_argument, NULL, 'r' }, - { "object-directory", required_argument, NULL, 'o' }, - { "object-file", required_argument, NULL, 'o' }, - { "source-prefix", required_argument, NULL, 's' }, - { "unconditional-branches", no_argument, NULL, 'u' }, - { "display-progress", no_argument, NULL, 'd' }, - { "hash-filenames", no_argument, NULL, 'x' }, - { 0, 0, 0, 0 } -}; - -/* Process args, return index to first non-arg. */ - -static int -process_args (int argc, char **argv) -{ - int opt; - - const char *opts = "abcdfhilmno:prs:uvx"; - while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1) - { - switch (opt) - { - case 'a': - flag_all_blocks = 1; - break; - case 'b': - flag_branches = 1; - break; - case 'c': - flag_counts = 1; - break; - case 'f': - flag_function_summary = 1; - break; - case 'h': - print_usage (false); - /* print_usage will exit. */ - case 'l': - flag_long_names = 1; - break; - case 'm': - flag_demangled_names = 1; - break; - case 'n': - flag_gcov_file = 0; - break; - case 'o': - object_directory = optarg; - break; - case 's': - source_prefix = optarg; - source_length = strlen (source_prefix); - break; - case 'r': - flag_relative_only = 1; - break; - case 'p': - flag_preserve_paths = 1; - break; - case 'u': - flag_unconditional = 1; - break; - case 'i': - flag_intermediate_format = 1; - flag_gcov_file = 1; - break; - case 'd': - flag_display_progress = 1; - break; - case 'x': - flag_hash_filenames = 1; - break; - case 'v': - print_version (); - /* print_version will exit. */ - default: - print_usage (true); - /* print_usage will exit. */ - } - } - - return optind; -} - -/* Output the result in intermediate format used by 'lcov'. - -The intermediate format contains a single file named 'foo.cc.gcov', -with no source code included. A sample output is - -file:foo.cc -function:5,1,_Z3foov -function:13,1,main -function:19,1,_GLOBAL__sub_I__Z3foov -function:19,1,_Z41__static_initialization_and_destruction_0ii -lcount:5,1 -lcount:7,9 -lcount:9,8 -lcount:11,1 -file:/.../iostream -lcount:74,1 -file:/.../basic_ios.h -file:/.../ostream -file:/.../ios_base.h -function:157,0,_ZStorSt12_Ios_IostateS_ -lcount:157,0 -file:/.../char_traits.h -function:258,0,_ZNSt11char_traitsIcE6lengthEPKc -lcount:258,0 -... - -The default gcov outputs multiple files: 'foo.cc.gcov', -'iostream.gcov', 'ios_base.h.gcov', etc. with source code -included. Instead the intermediate format here outputs only a single -file 'foo.cc.gcov' similar to the above example. */ - -static void -output_intermediate_file (FILE *gcov_file, source_t *src) -{ - unsigned line_num; /* current line number. */ - const line_t *line; /* current line info ptr. */ - function_t *fn; /* current function info ptr. */ - - fprintf (gcov_file, "file:%s\n", src->name); /* source file name */ - - for (fn = src->functions; fn; fn = fn->next_file_fn) - { - /* function:<name>,<line_number>,<execution_count> */ - fprintf (gcov_file, "function:%d,%s,%s\n", fn->line, - format_gcov (fn->blocks[0].count, 0, -1), - flag_demangled_names ? fn->demangled_name : fn->name); - } - - for (line_num = 1, line = &src->lines[line_num]; - line_num < src->num_lines; - line_num++, line++) - { - arc_t *arc; - if (line->exists) - fprintf (gcov_file, "lcount:%u,%s\n", line_num, - format_gcov (line->count, 0, -1)); - if (flag_branches) - for (arc = line->u.branches; arc; arc = arc->line_next) - { - if (!arc->is_unconditional && !arc->is_call_non_return) - { - const char *branch_type; - /* branch:<line_num>,<branch_coverage_type> - branch_coverage_type - : notexec (Branch not executed) - : taken (Branch executed and taken) - : nottaken (Branch executed, but not taken) - */ - if (arc->src->count) - branch_type = (arc->count > 0) ? "taken" : "nottaken"; - else - branch_type = "notexec"; - fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type); - } - } - } -} - -/* Process a single input file. */ - -static void -process_file (const char *file_name) -{ - function_t *fns; - - create_file_names (file_name); - fns = read_graph_file (); - if (!fns) - return; - - read_count_file (fns); - while (fns) - { - function_t *fn = fns; - - fns = fn->next; - fn->next = NULL; - if (fn->counts || no_data_file) - { - unsigned src = fn->src; - unsigned line = fn->line; - unsigned block_no; - function_t *probe, **prev; - - /* Now insert it into the source file's list of - functions. Normally functions will be encountered in - ascending order, so a simple scan is quick. Note we're - building this list in reverse order. */ - for (prev = &sources[src].functions; - (probe = *prev); prev = &probe->next_file_fn) - if (probe->line <= line) - break; - fn->next_file_fn = probe; - *prev = fn; - - /* Mark last line in files touched by function. */ - for (block_no = 0; block_no != fn->num_blocks; block_no++) - { - unsigned *enc = fn->blocks[block_no].u.line.encoding; - unsigned num = fn->blocks[block_no].u.line.num; - - for (; num--; enc++) - if (!*enc) - { - if (enc[1] != src) - { - if (line >= sources[src].num_lines) - sources[src].num_lines = line + 1; - line = 0; - src = enc[1]; - } - enc++; - num--; - } - else if (*enc > line) - line = *enc; - } - if (line >= sources[src].num_lines) - sources[src].num_lines = line + 1; - - solve_flow_graph (fn); - if (fn->has_catch) - find_exception_blocks (fn); - *fn_end = fn; - fn_end = &fn->next; - } - else - /* The function was not in the executable -- some other - instance must have been selected. */ - release_function (fn); - } -} - -static void -output_gcov_file (const char *file_name, source_t *src) -{ - char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name); - - if (src->coverage.lines) - { - FILE *gcov_file = fopen (gcov_file_name, "w"); - if (gcov_file) - { - fnotice (stdout, "Creating '%s'\n", gcov_file_name); - - if (flag_intermediate_format) - output_intermediate_file (gcov_file, src); - else - output_lines (gcov_file, src); - if (ferror (gcov_file)) - fnotice (stderr, "Error writing output file '%s'\n", gcov_file_name); - fclose (gcov_file); - } - else - fnotice (stderr, "Could not open output file '%s'\n", gcov_file_name); - } - else - { - unlink (gcov_file_name); - fnotice (stdout, "Removing '%s'\n", gcov_file_name); - } - free (gcov_file_name); -} - -static void -generate_results (const char *file_name) -{ - unsigned ix; - source_t *src; - function_t *fn; - - for (ix = n_sources, src = sources; ix--; src++) - if (src->num_lines) - src->lines = XCNEWVEC (line_t, src->num_lines); - - for (fn = functions; fn; fn = fn->next) - { - coverage_t coverage; - - memset (&coverage, 0, sizeof (coverage)); - coverage.name = flag_demangled_names ? fn->demangled_name : fn->name; - add_line_counts (flag_function_summary ? &coverage : NULL, fn); - if (flag_function_summary) - { - function_summary (&coverage, "Function"); - fnotice (stdout, "\n"); - } - } - - if (file_name) - { - name_map_t *name_map = (name_map_t *)bsearch - (file_name, names, n_names, sizeof (*names), name_search); - if (name_map) - file_name = sources[name_map->src].coverage.name; - else - file_name = canonicalize_name (file_name); - } - - for (ix = n_sources, src = sources; ix--; src++) - { - if (flag_relative_only) - { - /* Ignore this source, if it is an absolute path (after - source prefix removal). */ - char first = src->coverage.name[0]; - -#if HAVE_DOS_BASED_FILE_SYSTEM - if (first && src->coverage.name[1] == ':') - first = src->coverage.name[2]; -#endif - if (IS_DIR_SEPARATOR (first)) - continue; - } - - accumulate_line_counts (src); - function_summary (&src->coverage, "File"); - total_lines += src->coverage.lines; - total_executed += src->coverage.lines_executed; - if (flag_gcov_file) - { - output_gcov_file (file_name, src); - fnotice (stdout, "\n"); - } - } - - if (!file_name) - executed_summary (total_lines, total_executed); -} - -/* Release a function structure */ - -static void -release_function (function_t *fn) -{ - unsigned ix; - block_t *block; - - for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) - { - arc_t *arc, *arc_n; - - for (arc = block->succ; arc; arc = arc_n) - { - arc_n = arc->succ_next; - free (arc); - } - } - free (fn->blocks); - free (fn->counts); - if (flag_demangled_names && fn->demangled_name != fn->name) - free (fn->demangled_name); - free (fn->name); -} - -/* Release all memory used. */ - -static void -release_structures (void) -{ - unsigned ix; - function_t *fn; - - for (ix = n_sources; ix--;) - free (sources[ix].lines); - free (sources); - - for (ix = n_names; ix--;) - free (names[ix].name); - free (names); - - while ((fn = functions)) - { - functions = fn->next; - release_function (fn); - } -} - -/* Generate the names of the graph and data files. If OBJECT_DIRECTORY - is not specified, these are named from FILE_NAME sans extension. If - OBJECT_DIRECTORY is specified and is a directory, the files are in that - directory, but named from the basename of the FILE_NAME, sans extension. - Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file* - and the data files are named from that. */ - -static void -create_file_names (const char *file_name) -{ - char *cptr; - char *name; - int length = strlen (file_name); - int base; - - /* Free previous file names. */ - free (bbg_file_name); - free (da_file_name); - da_file_name = bbg_file_name = NULL; - bbg_file_time = 0; - bbg_stamp = 0; - - if (object_directory && object_directory[0]) - { - struct stat status; - - length += strlen (object_directory) + 2; - name = XNEWVEC (char, length); - name[0] = 0; - - base = !stat (object_directory, &status) && S_ISDIR (status.st_mode); - strcat (name, object_directory); - if (base && (!IS_DIR_SEPARATOR (name[strlen (name) - 1]))) - strcat (name, "/"); - } - else - { - name = XNEWVEC (char, length + 1); - strcpy (name, file_name); - base = 0; - } - - if (base) - { - /* Append source file name. */ - const char *cptr = lbasename (file_name); - strcat (name, cptr ? cptr : file_name); - } - - /* Remove the extension. */ - cptr = strrchr (CONST_CAST (char *, lbasename (name)), '.'); - if (cptr) - *cptr = 0; - - length = strlen (name); - - bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1); - strcpy (bbg_file_name, name); - strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX); - - da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1); - strcpy (da_file_name, name); - strcpy (da_file_name + length, GCOV_DATA_SUFFIX); - - free (name); - return; -} - -/* A is a string and B is a pointer to name_map_t. Compare for file - name orderability. */ - -static int -name_search (const void *a_, const void *b_) -{ - const char *a = (const char *)a_; - const name_map_t *b = (const name_map_t *)b_; - -#if HAVE_DOS_BASED_FILE_SYSTEM - return strcasecmp (a, b->name); -#else - return strcmp (a, b->name); -#endif -} - -/* A and B are a pointer to name_map_t. Compare for file name - orderability. */ - -static int -name_sort (const void *a_, const void *b_) -{ - const name_map_t *a = (const name_map_t *)a_; - return name_search (a->name, b_); -} - -/* Find or create a source file structure for FILE_NAME. Copies - FILE_NAME on creation */ - -static unsigned -find_source (const char *file_name) -{ - name_map_t *name_map; - char *canon; - unsigned idx; - struct stat status; - - if (!file_name) - file_name = "<unknown>"; - name_map = (name_map_t *)bsearch - (file_name, names, n_names, sizeof (*names), name_search); - if (name_map) - { - idx = name_map->src; - goto check_date; - } - - if (n_names + 2 > a_names) - { - /* Extend the name map array -- we'll be inserting one or two - entries. */ - a_names *= 2; - name_map = XNEWVEC (name_map_t, a_names); - memcpy (name_map, names, n_names * sizeof (*names)); - free (names); - names = name_map; - } - - /* Not found, try the canonical name. */ - canon = canonicalize_name (file_name); - name_map = (name_map_t *) bsearch (canon, names, n_names, sizeof (*names), - name_search); - if (!name_map) - { - /* Not found with canonical name, create a new source. */ - source_t *src; - - if (n_sources == a_sources) - { - a_sources *= 2; - src = XNEWVEC (source_t, a_sources); - memcpy (src, sources, n_sources * sizeof (*sources)); - free (sources); - sources = src; - } - - idx = n_sources; - - name_map = &names[n_names++]; - name_map->name = canon; - name_map->src = idx; - - src = &sources[n_sources++]; - memset (src, 0, sizeof (*src)); - src->name = canon; - src->coverage.name = src->name; - if (source_length -#if HAVE_DOS_BASED_FILE_SYSTEM - /* You lose if separators don't match exactly in the - prefix. */ - && !strncasecmp (source_prefix, src->coverage.name, source_length) -#else - && !strncmp (source_prefix, src->coverage.name, source_length) -#endif - && IS_DIR_SEPARATOR (src->coverage.name[source_length])) - src->coverage.name += source_length + 1; - if (!stat (src->name, &status)) - src->file_time = status.st_mtime; - } - else - idx = name_map->src; - - if (name_search (file_name, name_map)) - { - /* Append the non-canonical name. */ - name_map = &names[n_names++]; - name_map->name = xstrdup (file_name); - name_map->src = idx; - } - - /* Resort the name map. */ - qsort (names, n_names, sizeof (*names), name_sort); - - check_date: - if (sources[idx].file_time > bbg_file_time) - { - static int info_emitted; - - fnotice (stderr, "%s:source file is newer than notes file '%s'\n", - file_name, bbg_file_name); - if (!info_emitted) - { - fnotice (stderr, - "(the message is displayed only once per source file)\n"); - info_emitted = 1; - } - sources[idx].file_time = 0; - } - - return idx; -} - -/* Read the notes file. Return list of functions read -- in reverse order. */ - -static function_t * -read_graph_file (void) -{ - unsigned version; - unsigned current_tag = 0; - function_t *fn = NULL; - function_t *fns = NULL; - function_t **fns_end = &fns; - unsigned src_idx = 0; - unsigned ix; - unsigned tag; - - if (!gcov_open (bbg_file_name, 1)) - { - fnotice (stderr, "%s:cannot open notes file\n", bbg_file_name); - return fns; - } - bbg_file_time = gcov_time (); - if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC)) - { - fnotice (stderr, "%s:not a gcov notes file\n", bbg_file_name); - gcov_close (); - return fns; - } - - version = gcov_read_unsigned (); - if (version != GCOV_VERSION) - { - char v[4], e[4]; - - GCOV_UNSIGNED2STRING (v, version); - GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - - fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n", - bbg_file_name, v, e); - } - bbg_stamp = gcov_read_unsigned (); - - while ((tag = gcov_read_unsigned ())) - { - unsigned length = gcov_read_unsigned (); - gcov_position_t base = gcov_position (); - - if (tag == GCOV_TAG_FUNCTION) - { - char *function_name; - unsigned ident, lineno; - unsigned lineno_checksum, cfg_checksum; - - ident = gcov_read_unsigned (); - lineno_checksum = gcov_read_unsigned (); - cfg_checksum = gcov_read_unsigned (); - function_name = xstrdup (gcov_read_string ()); - src_idx = find_source (gcov_read_string ()); - lineno = gcov_read_unsigned (); - - fn = XCNEW (function_t); - fn->name = function_name; - if (flag_demangled_names) - { - fn->demangled_name = cplus_demangle (fn->name, DMGL_PARAMS); - if (!fn->demangled_name) - fn->demangled_name = fn->name; - } - fn->ident = ident; - fn->lineno_checksum = lineno_checksum; - fn->cfg_checksum = cfg_checksum; - fn->src = src_idx; - fn->line = lineno; - - fn->next_file_fn = NULL; - fn->next = NULL; - *fns_end = fn; - fns_end = &fn->next; - current_tag = tag; - } - else if (fn && tag == GCOV_TAG_BLOCKS) - { - if (fn->blocks) - fnotice (stderr, "%s:already seen blocks for '%s'\n", - bbg_file_name, fn->name); - else - { - unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length); - fn->num_blocks = num_blocks; - - fn->blocks = XCNEWVEC (block_t, fn->num_blocks); - for (ix = 0; ix != num_blocks; ix++) - fn->blocks[ix].flags = gcov_read_unsigned (); - } - } - else if (fn && tag == GCOV_TAG_ARCS) - { - unsigned src = gcov_read_unsigned (); - unsigned num_dests = GCOV_TAG_ARCS_NUM (length); - block_t *src_blk = &fn->blocks[src]; - unsigned mark_catches = 0; - struct arc_info *arc; - - if (src >= fn->num_blocks || fn->blocks[src].succ) - goto corrupt; - - while (num_dests--) - { - unsigned dest = gcov_read_unsigned (); - unsigned flags = gcov_read_unsigned (); - - if (dest >= fn->num_blocks) - goto corrupt; - arc = XCNEW (arc_t); - - arc->dst = &fn->blocks[dest]; - arc->src = src_blk; - - arc->count = 0; - arc->count_valid = 0; - arc->on_tree = !!(flags & GCOV_ARC_ON_TREE); - arc->fake = !!(flags & GCOV_ARC_FAKE); - arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH); - - arc->succ_next = src_blk->succ; - src_blk->succ = arc; - src_blk->num_succ++; - - arc->pred_next = fn->blocks[dest].pred; - fn->blocks[dest].pred = arc; - fn->blocks[dest].num_pred++; - - if (arc->fake) - { - if (src) - { - /* Exceptional exit from this function, the - source block must be a call. */ - fn->blocks[src].is_call_site = 1; - arc->is_call_non_return = 1; - mark_catches = 1; - } - else - { - /* Non-local return from a callee of this - function. The destination block is a setjmp. */ - arc->is_nonlocal_return = 1; - fn->blocks[dest].is_nonlocal_return = 1; - } - } - - if (!arc->on_tree) - fn->num_counts++; - } - - if (mark_catches) - { - /* We have a fake exit from this block. The other - non-fall through exits must be to catch handlers. - Mark them as catch arcs. */ - - for (arc = src_blk->succ; arc; arc = arc->succ_next) - if (!arc->fake && !arc->fall_through) - { - arc->is_throw = 1; - fn->has_catch = 1; - } - } - } - else if (fn && tag == GCOV_TAG_LINES) - { - unsigned blockno = gcov_read_unsigned (); - unsigned *line_nos = XCNEWVEC (unsigned, length - 1); - - if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding) - goto corrupt; - - for (ix = 0; ; ) - { - unsigned lineno = gcov_read_unsigned (); - - if (lineno) - { - if (!ix) - { - line_nos[ix++] = 0; - line_nos[ix++] = src_idx; - } - line_nos[ix++] = lineno; - } - else - { - const char *file_name = gcov_read_string (); - - if (!file_name) - break; - src_idx = find_source (file_name); - line_nos[ix++] = 0; - line_nos[ix++] = src_idx; - } - } - - fn->blocks[blockno].u.line.encoding = line_nos; - fn->blocks[blockno].u.line.num = ix; - } - else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag)) - { - fn = NULL; - current_tag = 0; - } - gcov_sync (base, length); - if (gcov_is_error ()) - { - corrupt:; - fnotice (stderr, "%s:corrupted\n", bbg_file_name); - break; - } - } - gcov_close (); - - if (!fns) - fnotice (stderr, "%s:no functions found\n", bbg_file_name); - - return fns; -} - -/* Reads profiles from the count file and attach to each - function. Return nonzero if fatal error. */ - -static int -read_count_file (function_t *fns) -{ - unsigned ix; - unsigned version; - unsigned tag; - function_t *fn = NULL; - int error = 0; - - if (!gcov_open (da_file_name, 1)) - { - fnotice (stderr, "%s:cannot open data file, assuming not executed\n", - da_file_name); - no_data_file = 1; - return 0; - } - if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) - { - fnotice (stderr, "%s:not a gcov data file\n", da_file_name); - cleanup:; - gcov_close (); - return 1; - } - version = gcov_read_unsigned (); - if (version != GCOV_VERSION) - { - char v[4], e[4]; - - GCOV_UNSIGNED2STRING (v, version); - GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - - fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n", - da_file_name, v, e); - } - tag = gcov_read_unsigned (); - if (tag != bbg_stamp) - { - fnotice (stderr, "%s:stamp mismatch with notes file\n", da_file_name); - goto cleanup; - } - - while ((tag = gcov_read_unsigned ())) - { - unsigned length = gcov_read_unsigned (); - unsigned long base = gcov_position (); - - if (tag == GCOV_TAG_PROGRAM_SUMMARY) - { - struct gcov_summary summary; - gcov_read_summary (&summary); - object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs; - program_count++; - } - else if (tag == GCOV_TAG_FUNCTION && !length) - ; /* placeholder */ - else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH) - { - unsigned ident; - struct function_info *fn_n; - - /* Try to find the function in the list. To speed up the - search, first start from the last function found. */ - ident = gcov_read_unsigned (); - fn_n = fns; - for (fn = fn ? fn->next : NULL; ; fn = fn->next) - { - if (fn) - ; - else if ((fn = fn_n)) - fn_n = NULL; - else - { - fnotice (stderr, "%s:unknown function '%u'\n", - da_file_name, ident); - break; - } - if (fn->ident == ident) - break; - } - - if (!fn) - ; - else if (gcov_read_unsigned () != fn->lineno_checksum - || gcov_read_unsigned () != fn->cfg_checksum) - { - mismatch:; - fnotice (stderr, "%s:profile mismatch for '%s'\n", - da_file_name, fn->name); - goto cleanup; - } - } - else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn) - { - if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts)) - goto mismatch; - - if (!fn->counts) - fn->counts = XCNEWVEC (gcov_type, fn->num_counts); - - for (ix = 0; ix != fn->num_counts; ix++) - fn->counts[ix] += gcov_read_counter (); - } - gcov_sync (base, length); - if ((error = gcov_is_error ())) - { - fnotice (stderr, - error < 0 - ? N_("%s:overflowed\n") - : N_("%s:corrupted\n"), - da_file_name); - goto cleanup; - } - } - - gcov_close (); - return 0; -} - -/* Solve the flow graph. Propagate counts from the instrumented arcs - to the blocks and the uninstrumented arcs. */ - -static void -solve_flow_graph (function_t *fn) -{ - unsigned ix; - arc_t *arc; - gcov_type *count_ptr = fn->counts; - block_t *blk; - block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */ - block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */ - - /* The arcs were built in reverse order. Fix that now. */ - for (ix = fn->num_blocks; ix--;) - { - arc_t *arc_p, *arc_n; - - for (arc_p = NULL, arc = fn->blocks[ix].succ; arc; - arc_p = arc, arc = arc_n) - { - arc_n = arc->succ_next; - arc->succ_next = arc_p; - } - fn->blocks[ix].succ = arc_p; - - for (arc_p = NULL, arc = fn->blocks[ix].pred; arc; - arc_p = arc, arc = arc_n) - { - arc_n = arc->pred_next; - arc->pred_next = arc_p; - } - fn->blocks[ix].pred = arc_p; - } - - if (fn->num_blocks < 2) - fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n", - bbg_file_name, fn->name); - else - { - if (fn->blocks[ENTRY_BLOCK].num_pred) - fnotice (stderr, "%s:'%s' has arcs to entry block\n", - bbg_file_name, fn->name); - else - /* We can't deduce the entry block counts from the lack of - predecessors. */ - fn->blocks[ENTRY_BLOCK].num_pred = ~(unsigned)0; - - if (fn->blocks[EXIT_BLOCK].num_succ) - fnotice (stderr, "%s:'%s' has arcs from exit block\n", - bbg_file_name, fn->name); - else - /* Likewise, we can't deduce exit block counts from the lack - of its successors. */ - fn->blocks[EXIT_BLOCK].num_succ = ~(unsigned)0; - } - - /* Propagate the measured counts, this must be done in the same - order as the code in profile.c */ - for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++) - { - block_t const *prev_dst = NULL; - int out_of_order = 0; - int non_fake_succ = 0; - - for (arc = blk->succ; arc; arc = arc->succ_next) - { - if (!arc->fake) - non_fake_succ++; - - if (!arc->on_tree) - { - if (count_ptr) - arc->count = *count_ptr++; - arc->count_valid = 1; - blk->num_succ--; - arc->dst->num_pred--; - } - if (prev_dst && prev_dst > arc->dst) - out_of_order = 1; - prev_dst = arc->dst; - } - if (non_fake_succ == 1) - { - /* If there is only one non-fake exit, it is an - unconditional branch. */ - for (arc = blk->succ; arc; arc = arc->succ_next) - if (!arc->fake) - { - arc->is_unconditional = 1; - /* If this block is instrumenting a call, it might be - an artificial block. It is not artificial if it has - a non-fallthrough exit, or the destination of this - arc has more than one entry. Mark the destination - block as a return site, if none of those conditions - hold. */ - if (blk->is_call_site && arc->fall_through - && arc->dst->pred == arc && !arc->pred_next) - arc->dst->is_call_return = 1; - } - } - - /* Sort the successor arcs into ascending dst order. profile.c - normally produces arcs in the right order, but sometimes with - one or two out of order. We're not using a particularly - smart sort. */ - if (out_of_order) - { - arc_t *start = blk->succ; - unsigned changes = 1; - - while (changes) - { - arc_t *arc, *arc_p, *arc_n; - - changes = 0; - for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);) - { - if (arc->dst > arc_n->dst) - { - changes = 1; - if (arc_p) - arc_p->succ_next = arc_n; - else - start = arc_n; - arc->succ_next = arc_n->succ_next; - arc_n->succ_next = arc; - arc_p = arc_n; - } - else - { - arc_p = arc; - arc = arc_n; - } - } - } - blk->succ = start; - } - - /* Place it on the invalid chain, it will be ignored if that's - wrong. */ - blk->invalid_chain = 1; - blk->chain = invalid_blocks; - invalid_blocks = blk; - } - - while (invalid_blocks || valid_blocks) - { - while ((blk = invalid_blocks)) - { - gcov_type total = 0; - const arc_t *arc; - - invalid_blocks = blk->chain; - blk->invalid_chain = 0; - if (!blk->num_succ) - for (arc = blk->succ; arc; arc = arc->succ_next) - total += arc->count; - else if (!blk->num_pred) - for (arc = blk->pred; arc; arc = arc->pred_next) - total += arc->count; - else - continue; - - blk->count = total; - blk->count_valid = 1; - blk->chain = valid_blocks; - blk->valid_chain = 1; - valid_blocks = blk; - } - while ((blk = valid_blocks)) - { - gcov_type total; - arc_t *arc, *inv_arc; - - valid_blocks = blk->chain; - blk->valid_chain = 0; - if (blk->num_succ == 1) - { - block_t *dst; - - total = blk->count; - inv_arc = NULL; - for (arc = blk->succ; arc; arc = arc->succ_next) - { - total -= arc->count; - if (!arc->count_valid) - inv_arc = arc; - } - dst = inv_arc->dst; - inv_arc->count_valid = 1; - inv_arc->count = total; - blk->num_succ--; - dst->num_pred--; - if (dst->count_valid) - { - if (dst->num_pred == 1 && !dst->valid_chain) - { - dst->chain = valid_blocks; - dst->valid_chain = 1; - valid_blocks = dst; - } - } - else - { - if (!dst->num_pred && !dst->invalid_chain) - { - dst->chain = invalid_blocks; - dst->invalid_chain = 1; - invalid_blocks = dst; - } - } - } - if (blk->num_pred == 1) - { - block_t *src; - - total = blk->count; - inv_arc = NULL; - for (arc = blk->pred; arc; arc = arc->pred_next) - { - total -= arc->count; - if (!arc->count_valid) - inv_arc = arc; - } - src = inv_arc->src; - inv_arc->count_valid = 1; - inv_arc->count = total; - blk->num_pred--; - src->num_succ--; - if (src->count_valid) - { - if (src->num_succ == 1 && !src->valid_chain) - { - src->chain = valid_blocks; - src->valid_chain = 1; - valid_blocks = src; - } - } - else - { - if (!src->num_succ && !src->invalid_chain) - { - src->chain = invalid_blocks; - src->invalid_chain = 1; - invalid_blocks = src; - } - } - } - } - } - - /* If the graph has been correctly solved, every block will have a - valid count. */ - for (ix = 0; ix < fn->num_blocks; ix++) - if (!fn->blocks[ix].count_valid) - { - fnotice (stderr, "%s:graph is unsolvable for '%s'\n", - bbg_file_name, fn->name); - break; - } -} - -/* Mark all the blocks only reachable via an incoming catch. */ - -static void -find_exception_blocks (function_t *fn) -{ - unsigned ix; - block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks); - - /* First mark all blocks as exceptional. */ - for (ix = fn->num_blocks; ix--;) - fn->blocks[ix].exceptional = 1; - - /* Now mark all the blocks reachable via non-fake edges */ - queue[0] = fn->blocks; - queue[0]->exceptional = 0; - for (ix = 1; ix;) - { - block_t *block = queue[--ix]; - const arc_t *arc; - - for (arc = block->succ; arc; arc = arc->succ_next) - if (!arc->fake && !arc->is_throw && arc->dst->exceptional) - { - arc->dst->exceptional = 0; - queue[ix++] = arc->dst; - } - } -} - - -/* Increment totals in COVERAGE according to arc ARC. */ - -static void -add_branch_counts (coverage_t *coverage, const arc_t *arc) -{ - if (arc->is_call_non_return) - { - coverage->calls++; - if (arc->src->count) - coverage->calls_executed++; - } - else if (!arc->is_unconditional) - { - coverage->branches++; - if (arc->src->count) - coverage->branches_executed++; - if (arc->count) - coverage->branches_taken++; - } -} - -/* Format a GCOV_TYPE integer as either a percent ratio, or absolute - count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places. - If DP is zero, no decimal point is printed. Only print 100% when - TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply - format TOP. Return pointer to a static string. */ - -static char const * -format_gcov (gcov_type top, gcov_type bottom, int dp) -{ - static char buffer[20]; - - if (dp >= 0) - { - float ratio = bottom ? (float)top / bottom : 0; - int ix; - unsigned limit = 100; - unsigned percent; - - for (ix = dp; ix--; ) - limit *= 10; - - percent = (unsigned) (ratio * limit + (float)0.5); - if (percent <= 0 && top) - percent = 1; - else if (percent >= limit && top != bottom) - percent = limit - 1; - ix = sprintf (buffer, "%.*u%%", dp + 1, percent); - if (dp) - { - dp++; - do - { - buffer[ix+1] = buffer[ix]; - ix--; - } - while (dp--); - buffer[ix + 1] = '.'; - } - } - else - sprintf (buffer, "%" PRId64, (int64_t)top); - - return buffer; -} - -/* Summary of execution */ - -static void -executed_summary (unsigned lines, unsigned executed) -{ - if (lines) - fnotice (stdout, "Lines executed:%s of %d\n", - format_gcov (executed, lines, 2), lines); - else - fnotice (stdout, "No executable lines\n"); -} - -/* Output summary info for a function or file. */ - -static void -function_summary (const coverage_t *coverage, const char *title) -{ - fnotice (stdout, "%s '%s'\n", title, coverage->name); - executed_summary (coverage->lines, coverage->lines_executed); - - if (flag_branches) - { - if (coverage->branches) - { - fnotice (stdout, "Branches executed:%s of %d\n", - format_gcov (coverage->branches_executed, - coverage->branches, 2), - coverage->branches); - fnotice (stdout, "Taken at least once:%s of %d\n", - format_gcov (coverage->branches_taken, - coverage->branches, 2), - coverage->branches); - } - else - fnotice (stdout, "No branches\n"); - if (coverage->calls) - fnotice (stdout, "Calls executed:%s of %d\n", - format_gcov (coverage->calls_executed, coverage->calls, 2), - coverage->calls); - else - fnotice (stdout, "No calls\n"); - } -} - -/* Canonicalize the filename NAME by canonicalizing directory - separators, eliding . components and resolving .. components - appropriately. Always returns a unique string. */ - -static char * -canonicalize_name (const char *name) -{ - /* The canonical name cannot be longer than the incoming name. */ - char *result = XNEWVEC (char, strlen (name) + 1); - const char *base = name, *probe; - char *ptr = result; - char *dd_base; - int slash = 0; - -#if HAVE_DOS_BASED_FILE_SYSTEM - if (base[0] && base[1] == ':') - { - result[0] = base[0]; - result[1] = ':'; - base += 2; - ptr += 2; - } -#endif - for (dd_base = ptr; *base; base = probe) - { - size_t len; - - for (probe = base; *probe; probe++) - if (IS_DIR_SEPARATOR (*probe)) - break; - - len = probe - base; - if (len == 1 && base[0] == '.') - /* Elide a '.' directory */ - ; - else if (len == 2 && base[0] == '.' && base[1] == '.') - { - /* '..', we can only elide it and the previous directory, if - we're not a symlink. */ - struct stat ATTRIBUTE_UNUSED buf; - - *ptr = 0; - if (dd_base == ptr -#if defined (S_ISLNK) - /* S_ISLNK is not POSIX.1-1996. */ - || stat (result, &buf) || S_ISLNK (buf.st_mode) -#endif - ) - { - /* Cannot elide, or unreadable or a symlink. */ - dd_base = ptr + 2 + slash; - goto regular; - } - while (ptr != dd_base && *ptr != '/') - ptr--; - slash = ptr != result; - } - else - { - regular: - /* Regular pathname component. */ - if (slash) - *ptr++ = '/'; - memcpy (ptr, base, len); - ptr += len; - slash = 1; - } - - for (; IS_DIR_SEPARATOR (*probe); probe++) - continue; - } - *ptr = 0; - - return result; -} - -/* Print hex representation of 16 bytes from SUM and write it to BUFFER. */ - -static void -md5sum_to_hex (const char *sum, char *buffer) -{ - for (unsigned i = 0; i < 16; i++) - sprintf (buffer + (2 * i), "%02x", (unsigned char)sum[i]); -} - -/* Generate an output file name. INPUT_NAME is the canonicalized main - input file and SRC_NAME is the canonicalized file name. - LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation. With - long_output_names we prepend the processed name of the input file - to each output name (except when the current source file is the - input file, so you don't get a double concatenation). The two - components are separated by '##'. With preserve_paths we create a - filename from all path components of the source file, replacing '/' - with '#', and .. with '^', without it we simply take the basename - component. (Remember, the canonicalized name will already have - elided '.' components and converted \\ separators.) */ - -static char * -make_gcov_file_name (const char *input_name, const char *src_name) -{ - char *ptr; - char *result; - - if (flag_long_names && input_name && strcmp (src_name, input_name)) - { - /* Generate the input filename part. */ - result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10); - - ptr = result; - ptr = mangle_name (input_name, ptr); - ptr[0] = ptr[1] = '#'; - ptr += 2; - } - else - { - result = XNEWVEC (char, strlen (src_name) + 10); - ptr = result; - } - - ptr = mangle_name (src_name, ptr); - strcpy (ptr, ".gcov"); - - /* When hashing filenames, we shorten them by only using the filename - component and appending a hash of the full (mangled) pathname. */ - if (flag_hash_filenames) - { - md5_ctx ctx; - char md5sum[16]; - char md5sum_hex[33]; - - md5_init_ctx (&ctx); - md5_process_bytes (src_name, strlen (src_name), &ctx); - md5_finish_ctx (&ctx, md5sum); - md5sum_to_hex (md5sum, md5sum_hex); - free (result); - - result = XNEWVEC (char, strlen (src_name) + 50); - ptr = result; - ptr = mangle_name (src_name, ptr); - ptr[0] = ptr[1] = '#'; - ptr += 2; - memcpy (ptr, md5sum_hex, 32); - ptr += 32; - strcpy (ptr, ".gcov"); - } - - return result; -} - -static char * -mangle_name (char const *base, char *ptr) -{ - size_t len; - - /* Generate the source filename part. */ - if (!flag_preserve_paths) - { - base = lbasename (base); - len = strlen (base); - memcpy (ptr, base, len); - ptr += len; - } - else - { - /* Convert '/' to '#', convert '..' to '^', - convert ':' to '~' on DOS based file system. */ - const char *probe; - -#if HAVE_DOS_BASED_FILE_SYSTEM - if (base[0] && base[1] == ':') - { - ptr[0] = base[0]; - ptr[1] = '~'; - ptr += 2; - base += 2; - } -#endif - for (; *base; base = probe) - { - size_t len; - - for (probe = base; *probe; probe++) - if (*probe == '/') - break; - len = probe - base; - if (len == 2 && base[0] == '.' && base[1] == '.') - *ptr++ = '^'; - else - { - memcpy (ptr, base, len); - ptr += len; - } - if (*probe) - { - *ptr++ = '#'; - probe++; - } - } - } - - return ptr; -} - -/* Scan through the bb_data for each line in the block, increment - the line number execution count indicated by the execution count of - the appropriate basic block. */ - -static void -add_line_counts (coverage_t *coverage, function_t *fn) -{ - unsigned ix; - line_t *line = NULL; /* This is propagated from one iteration to the - next. */ - - /* Scan each basic block. */ - for (ix = 0; ix != fn->num_blocks; ix++) - { - block_t *block = &fn->blocks[ix]; - unsigned *encoding; - const source_t *src = NULL; - unsigned jx; - - if (block->count && ix && ix + 1 != fn->num_blocks) - fn->blocks_executed++; - for (jx = 0, encoding = block->u.line.encoding; - jx != block->u.line.num; jx++, encoding++) - if (!*encoding) - { - src = &sources[*++encoding]; - jx++; - } - else - { - line = &src->lines[*encoding]; - - if (coverage) - { - if (!line->exists) - coverage->lines++; - if (!line->count && block->count) - coverage->lines_executed++; - } - line->exists = 1; - if (!block->exceptional) - line->unexceptional = 1; - line->count += block->count; - } - free (block->u.line.encoding); - block->u.cycle.arc = NULL; - block->u.cycle.ident = ~0U; - - if (!ix || ix + 1 == fn->num_blocks) - /* Entry or exit block */; - else if (flag_all_blocks) - { - line_t *block_line = line; - - if (!block_line) - block_line = &sources[fn->src].lines[fn->line]; - - block->chain = block_line->u.blocks; - block_line->u.blocks = block; - } - else if (flag_branches) - { - arc_t *arc; - - for (arc = block->succ; arc; arc = arc->succ_next) - { - arc->line_next = line->u.branches; - line->u.branches = arc; - if (coverage && !arc->is_unconditional) - add_branch_counts (coverage, arc); - } - } - } - if (!line) - fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); -} - -/* Accumulate the line counts of a file. */ - -static void -accumulate_line_counts (source_t *src) -{ - line_t *line; - function_t *fn, *fn_p, *fn_n; - unsigned ix; - - /* Reverse the function order. */ - for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n) - { - fn_n = fn->next_file_fn; - fn->next_file_fn = fn_p; - } - src->functions = fn_p; - - for (ix = src->num_lines, line = src->lines; ix--; line++) - { - if (!flag_all_blocks) - { - arc_t *arc, *arc_p, *arc_n; - - /* Total and reverse the branch information. */ - for (arc = line->u.branches, arc_p = NULL; arc; - arc_p = arc, arc = arc_n) - { - arc_n = arc->line_next; - arc->line_next = arc_p; - - add_branch_counts (&src->coverage, arc); - } - line->u.branches = arc_p; - } - else if (line->u.blocks) - { - /* The user expects the line count to be the number of times - a line has been executed. Simply summing the block count - will give an artificially high number. The Right Thing - is to sum the entry counts to the graph of blocks on this - line, then find the elementary cycles of the local graph - and add the transition counts of those cycles. */ - block_t *block, *block_p, *block_n; - gcov_type count = 0; - - /* Reverse the block information. */ - for (block = line->u.blocks, block_p = NULL; block; - block_p = block, block = block_n) - { - block_n = block->chain; - block->chain = block_p; - block->u.cycle.ident = ix; - } - line->u.blocks = block_p; - - /* Sum the entry arcs. */ - for (block = line->u.blocks; block; block = block->chain) - { - arc_t *arc; - - for (arc = block->pred; arc; arc = arc->pred_next) - if (flag_branches) - add_branch_counts (&src->coverage, arc); - } - - /* Cycle detection. */ - for (block = line->u.blocks; block; block = block->chain) - { - for (arc_t *arc = block->pred; arc; arc = arc->pred_next) - if (!line->has_block (arc->src)) - count += arc->count; - for (arc_t *arc = block->succ; arc; arc = arc->succ_next) - arc->cs_count = arc->count; - } - - /* Now, add the count of loops entirely on this line. */ - count += get_cycles_count (*line); - line->count = count; - } - - if (line->exists) - { - src->coverage.lines++; - if (line->count) - src->coverage.lines_executed++; - } - } -} - -/* Output information about ARC number IX. Returns nonzero if - anything is output. */ - -static int -output_branch_count (FILE *gcov_file, int ix, const arc_t *arc) -{ - if (arc->is_call_non_return) - { - if (arc->src->count) - { - fnotice (gcov_file, "call %2d returned %s\n", ix, - format_gcov (arc->src->count - arc->count, - arc->src->count, -flag_counts)); - } - else - fnotice (gcov_file, "call %2d never executed\n", ix); - } - else if (!arc->is_unconditional) - { - if (arc->src->count) - fnotice (gcov_file, "branch %2d taken %s%s\n", ix, - format_gcov (arc->count, arc->src->count, -flag_counts), - arc->fall_through ? " (fallthrough)" - : arc->is_throw ? " (throw)" : ""); - else - fnotice (gcov_file, "branch %2d never executed\n", ix); - } - else if (flag_unconditional && !arc->dst->is_call_return) - { - if (arc->src->count) - fnotice (gcov_file, "unconditional %2d taken %s\n", ix, - format_gcov (arc->count, arc->src->count, -flag_counts)); - else - fnotice (gcov_file, "unconditional %2d never executed\n", ix); - } - else - return 0; - return 1; -} - -static const char * -read_line (FILE *file) -{ - static char *string; - static size_t string_len; - size_t pos = 0; - char *ptr; - - if (!string_len) - { - string_len = 200; - string = XNEWVEC (char, string_len); - } - - while ((ptr = fgets (string + pos, string_len - pos, file))) - { - size_t len = strlen (string + pos); - - if (len && string[pos + len - 1] == '\n') - { - string[pos + len - 1] = 0; - return string; - } - pos += len; - /* If the file contains NUL characters or an incomplete - last line, which can happen more than once in one run, - we have to avoid doubling the STRING_LEN unnecessarily. */ - if (pos > string_len / 2) - { - string_len *= 2; - string = XRESIZEVEC (char, string, string_len); - } - } - - return pos ? string : NULL; -} - -/* Read in the source file one line at a time, and output that line to - the gcov file preceded by its execution count and other - information. */ - -static void -output_lines (FILE *gcov_file, const source_t *src) -{ - FILE *source_file; - unsigned line_num; /* current line number. */ - const line_t *line; /* current line info ptr. */ - const char *retval = ""; /* status of source file reading. */ - function_t *fn = NULL; - - fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name); - if (!multiple_files) - { - fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name); - fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, - no_data_file ? "-" : da_file_name); - fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs); - } - fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count); - - source_file = fopen (src->name, "r"); - if (!source_file) - { - fnotice (stderr, "Cannot open source file %s\n", src->name); - retval = NULL; - } - else if (src->file_time == 0) - fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0); - - if (flag_branches) - fn = src->functions; - - for (line_num = 1, line = &src->lines[line_num]; - line_num < src->num_lines; line_num++, line++) - { - for (; fn && fn->line == line_num; fn = fn->next_file_fn) - { - arc_t *arc = fn->blocks[EXIT_BLOCK].pred; - gcov_type return_count = fn->blocks[EXIT_BLOCK].count; - gcov_type called_count = fn->blocks[ENTRY_BLOCK].count; - - for (; arc; arc = arc->pred_next) - if (arc->fake) - return_count -= arc->count; - - fprintf (gcov_file, "function %s", flag_demangled_names ? - fn->demangled_name : fn->name); - fprintf (gcov_file, " called %s", - format_gcov (called_count, 0, -1)); - fprintf (gcov_file, " returned %s", - format_gcov (return_count, called_count, 0)); - fprintf (gcov_file, " blocks executed %s", - format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0)); - fprintf (gcov_file, "\n"); - } - - if (retval) - retval = read_line (source_file); - - /* For lines which don't exist in the .bb file, print '-' before - the source line. For lines which exist but were never - executed, print '#####' or '=====' before the source line. - Otherwise, print the execution count before the source line. - There are 16 spaces of indentation added before the source - line so that tabs won't be messed up. */ - fprintf (gcov_file, "%9s:%5u:%s\n", - !line->exists ? "-" : line->count - ? format_gcov (line->count, 0, -1) - : line->unexceptional ? "#####" : "=====", line_num, - retval ? retval : "/*EOF*/"); - - if (flag_all_blocks) - { - block_t *block; - arc_t *arc; - int ix, jx; - - for (ix = jx = 0, block = line->u.blocks; block; - block = block->chain) - { - if (!block->is_call_return) - fprintf (gcov_file, "%9s:%5u-block %2d\n", - !line->exists ? "-" : block->count - ? format_gcov (block->count, 0, -1) - : block->exceptional ? "%%%%%" : "$$$$$", - line_num, ix++); - if (flag_branches) - for (arc = block->succ; arc; arc = arc->succ_next) - jx += output_branch_count (gcov_file, jx, arc); - } - } - else if (flag_branches) - { - int ix; - arc_t *arc; - - for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next) - ix += output_branch_count (gcov_file, ix, arc); - } - } - - /* Handle all remaining source lines. There may be lines after the - last line of code. */ - if (retval) - { - for (; (retval = read_line (source_file)); line_num++) - fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval); - } - - if (source_file) - fclose (source_file); -} |