From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 16955 invoked by alias); 1 Nov 2017 08:00:34 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 14886 invoked by uid 89); 1 Nov 2017 08:00:33 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_PASS autolearn=ham version=3.3.2 spammy=Sum, sitting, counted, planned X-HELO: mx2.suse.de Received: from mx2.suse.de (HELO mx2.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 01 Nov 2017 08:00:21 +0000 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id F123DAAB6; Wed, 1 Nov 2017 08:00:18 +0000 (UTC) From: =?UTF-8?Q?Martin_Li=c5=a1ka?= Subject: [PATCH 8/N][RFC] v2 GCOV: support multiple functions per a line To: Nathan Sidwell , gcc-patches@gcc.gnu.org References: <60989f3c-46cf-8955-06f4-e3216290a2a8@suse.cz> <4a1cdc29-7771-75a9-4731-eba29253ab40@acm.org> Message-ID: Date: Wed, 01 Nov 2017 08:00:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.4.0 MIME-Version: 1.0 In-Reply-To: <4a1cdc29-7771-75a9-4731-eba29253ab40@acm.org> Content-Type: multipart/mixed; boundary="------------9DA2E967627CD5420CA13222" X-IsSubscribed: yes X-SW-Source: 2017-11/txt/msg00008.txt.bz2 This is a multi-part message in MIME format. --------------9DA2E967627CD5420CA13222 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Content-length: 4722 Hi. As shown from examples with -O2 optimization level, previous approach was not viable. A more complex approach is necessary and can be shortly described as follows: 1) each function has assigned start and end line in a source file 2) group functions are these functions that begin on a line and there's another function that begins on the same line (template initializations, deleting dtors, ...) 3) both source_file and function have 'vector lines': a) source_file::lines represent all lines of non-group functions b) function_info::lines exists if a function is a group function and represent lines that belong to the function 4) function_info::lines for a group of functions are summed to source_file::lines; that makes function output_lines easier 5) branches, calls and blocks are printed per function in a group, not to 'global scope' lines 6) DECL_ARTIFICIAL functions are ignored There are 2 examples that illustrate the approach: 1: 7:class ClassB -: 8:{ -: 9:public: -: 10: virtual ~ClassB (); -: 11:}; -: 12: 3: 13:ClassB::~ClassB () -: 14:{ 1: 15: int r = inline_me (glob); 1: 16: std::cout << "Bey, ClassB!" << r << std::endl; 2: 17:} ------------------ _ZN6ClassBD0Ev: 1: 13:ClassB::~ClassB () -: 14:{ -: 15: int r = inline_me (glob); -: 16: std::cout << "Bey, ClassB!" << r << std::endl; 1: 17:} ------------------ _ZN6ClassBD2Ev: 2: 13:ClassB::~ClassB () -: 14:{ 1: 15: int r = inline_me (glob); 1: 16: std::cout << "Bey, ClassB!" << r << std::endl; 1: 17:} ------------------ -: 18: -: 19:int 1: 20:main (int argc, char **argv) -: 21:{ 1: 22: ClassB *pObjB = new ClassB (); 1: 23: delete pObjB; -: 24: 1: 25: if (argc == 123) #####: 26: return 1; -: 27: -: 28: return 0; -: 29:} -: 30: -: 31: -: 32:static int #####: 33:inline_me(int a) -: 34:{ 1*: 35: return a * a; -: 36:} It's compiled with -O2 and it shows that ZN6ClassBD2Ev function executes also line 35, but the line is not counted to function_info::lines as it does not live in function scope. Note that line 35 is marked with '*'. That's because I used -fkeep-inline-functions. -: 0:Source:test.cpp -: 0:Graph:test.gcno -: 0:Data:test.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:template -: 2:class Foo -: 3:{ -: 4: public: 3: 5: Foo() -: 6: { 3: 7: b = 123; 3: 8: } ------------------ Foo::Foo(): #####: 5: Foo() -: 6: { #####: 7: b = 123; #####: 8: } ------------------ Foo::Foo(): 1: 5: Foo() -: 6: { 1: 7: b = 123; 1: 8: } ------------------ Foo::Foo(): 2: 5: Foo() -: 6: { 2: 7: b = 123; 2: 8: } ------------------ -: 9: 1: 10: void test() { if (!b) __builtin_abort (); b = 111; } ------------------ Foo::test(): #####: 10: void test() { if (!b) __builtin_abort (); b = 111; } %%%%%: 10-block 0 %%%%%: 10-block 1 ------------------ Foo::test(): 1*: 10: void test() { if (!b) __builtin_abort (); b = 111; } 1: 10-block 0 %%%%%: 10-block 1 ------------------ Foo::test(): #####: 10: void test() { if (!b) __builtin_abort (); b = 111; } %%%%%: 10-block 0 %%%%%: 10-block 1 ------------------ -: 11: -: 12: private: -: 13: int b; -: 14:}; -: 15: -: 16:template class Foo; -: 17:template class Foo; -: 18:template class Foo; -: 19: 1: 20:int main() -: 21:{ 1: 22: Foo xx; 1: 22-block 0 1: 23: Foo yy; 1: 23-block 0 1: 24: Foo zz; 1: 24-block 0 1: 25: xx.test(); 1: 25-block 0 -: 26: 1: 27: return 0; 1: 27-block 0 -: 28:} This sample is illustration of how we aggregate statistics. I've added changelog entry and I'm still planning to enhance documentation, add some tests (which would be tricky) and small follow-up clean-up is planned. In meantime I would appreciate a feedback. Thanks, Martin --------------9DA2E967627CD5420CA13222 Content-Type: text/x-patch; name="0001-GCOV-support-multiple-functions-per-a-line.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-GCOV-support-multiple-functions-per-a-line.patch" Content-length: 35857 >From cf05cc4b236d7fd58091a4fd3fd4e88bfee28c00 Mon Sep 17 00:00:00 2001 From: marxin Date: Thu, 26 Oct 2017 10:39:40 +0200 Subject: [PATCH] GCOV: support multiple functions per a line gcc/ChangeLog: 2017-11-01 Martin Liska * coverage.c (coverage_begin_function): Output also end locus of a function and information whether the function is artificial. * gcov-dump.c (tag_function): Parse and print the information. * gcov.c (INCLUDE_MAP): Add include. (INCLUDE_SET): Likewise. (struct line_info): Move earlier in the source file because of vector in function_info structure. (line_info::line_info): Likewise. (line_info::has_block): Likewise. (struct source_info): Add new member index. (source_info::get_functions_at_location): New function. (function_info::group_line_p): New function. (output_intermediate_line): New function. (output_intermediate_file): Use the mentioned function. (struct function_start): New. (struct function_start_pair_hash): Likewise. (process_file): Add code that identifies group functions. Assign lines either to global or function scope. (generate_results): Skip artificial functions. (find_source): Assign index for each source file. (read_graph_file): Read new flag artificial and end_line. (add_line_counts): Assign it either to global of function scope. (accumulate_line_counts): Isolate core of the function to accumulate_line_info and call it for both function and global scope lines. (accumulate_line_info): New function. (output_line_beginning): Fix GNU coding style. (print_source_line): New function. (output_line_details): Likewise. (output_function_details): Likewise. (output_lines): Iterate both source (global) scope and function scope. --- gcc/coverage.c | 2 + gcc/gcov-dump.c | 7 +- gcc/gcov.c | 823 ++++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 569 insertions(+), 263 deletions(-) diff --git a/gcc/coverage.c b/gcc/coverage.c index 8a56a677f15..8ac593920b6 100644 --- a/gcc/coverage.c +++ b/gcc/coverage.c @@ -663,8 +663,10 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum) gcov_write_unsigned (cfg_checksum); gcov_write_string (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); + gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl)); gcov_write_filename (xloc.file); gcov_write_unsigned (xloc.line); + gcov_write_unsigned (expand_location (cfun->function_end_locus).line); gcov_write_length (offset); return !gcov_is_error (); diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c index d24e72ac4a1..22d08c04c1e 100644 --- a/gcc/gcov-dump.c +++ b/gcc/gcov-dump.c @@ -308,9 +308,14 @@ tag_function (const char *filename ATTRIBUTE_UNUSED, name = gcov_read_string (); printf (", `%s'", name ? name : "NULL"); + unsigned artificial = gcov_read_unsigned (); name = gcov_read_string (); printf (" %s", name ? name : "NULL"); - printf (":%u", gcov_read_unsigned ()); + unsigned line_start = gcov_read_unsigned (); + unsigned line_end = gcov_read_unsigned (); + printf (":%u:%u", line_start, line_end); + if (artificial) + printf (", artificial"); } } } diff --git a/gcc/gcov.c b/gcc/gcov.c index f1e7777cf94..9a6c74cd2d6 100644 --- a/gcc/gcov.c +++ b/gcc/gcov.c @@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3. If not see #define INCLUDE_ALGORITHM #define INCLUDE_VECTOR #define INCLUDE_STRING +#define INCLUDE_MAP +#define INCLUDE_SET #include "system.h" #include "coretypes.h" #include "tm.h" @@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0), cycle.arc = NULL; } +/* Describes a single line of source. Contains a chain of basic blocks + with code on it. */ + +struct line_info +{ + /* Default constructor. */ + line_info (); + + /* Return true when NEEDLE is one of basic blocks the line belongs to. */ + bool has_block (block_t *needle); + + /* Execution count. */ + gcov_type count; + + /* Branches from blocks that end on this line. */ + vector branches; + + /* blocks which start on this line. Used in all-blocks mode. */ + vector blocks; + + unsigned exists : 1; + unsigned unexceptional : 1; + unsigned has_unexecuted_block : 1; +}; + +line_info::line_info (): count (0), branches (), blocks (), exists (false), + unexceptional (0), has_unexecuted_block (0) +{ +} + +bool +line_info::has_block (block_t *needle) +{ + return std::find (blocks.begin (), blocks.end (), needle) != blocks.end (); +} + /* Describes a single function. Contains an array of basic blocks. */ typedef struct function_info @@ -190,6 +228,10 @@ typedef struct function_info function_info (); ~function_info (); + /* Return true when line N belongs to the function in source file SRC_IDX. + The line must be defined in body of the function, can't be inlined. */ + bool group_line_p (unsigned n, unsigned src_idx); + /* Name of function. */ char *name; char *demangled_name; @@ -200,6 +242,13 @@ typedef struct function_info /* The graph contains at least one fake incoming edge. */ unsigned has_catch : 1; + /* True when the function is artificial and does not exist + in a source file. */ + unsigned artificial : 1; + + /* True when multiple functions start at a line in a source file. */ + unsigned is_group : 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) @@ -211,12 +260,17 @@ typedef struct function_info gcov_type *counts; unsigned num_counts; - /* First line number & file. */ - unsigned line; + /* First line number. */ + unsigned start_line; + + /* Last line number. */ + unsigned end_line; + + /* Index of source file where the function is defined. */ unsigned src; - /* Next function in same source file. */ - struct function_info *next_file_fn; + /* Vector of line information. */ + vector lines; /* Next function. */ struct function_info *next; @@ -239,42 +293,6 @@ typedef struct coverage_info char *name; } coverage_t; -/* Describes a single line of source. Contains a chain of basic blocks - with code on it. */ - -struct line_info -{ - /* Default constructor. */ - line_info (); - - /* Return true when NEEDLE is one of basic blocks the line belongs to. */ - bool has_block (block_t *needle); - - /* Execution count. */ - gcov_type count; - - /* Branches from blocks that end on this line. */ - vector branches; - - /* blocks which start on this line. Used in all-blocks mode. */ - vector blocks; - - unsigned exists : 1; - unsigned unexceptional : 1; - unsigned has_unexecuted_block : 1; -}; - -line_info::line_info (): count (0), branches (), blocks (), exists (false), - unexceptional (0), has_unexecuted_block (0) -{ -} - -bool -line_info::has_block (block_t *needle) -{ - return std::find (blocks.begin (), blocks.end (), needle) != blocks.end (); -} - /* Describes a file mentioned in the block graph. Contains an array of line info. */ @@ -283,6 +301,11 @@ struct source_info /* Default constructor. */ source_info (); + vector get_functions_at_location (unsigned line_num) const; + + /* Index of the source_info in sources vector. */ + unsigned index; + /* Canonical name of source file. */ char *name; time_t file_time; @@ -294,14 +317,29 @@ struct source_info /* Functions in this source file. These are in ascending line number order. */ - function_t *functions; + vector functions; }; -source_info::source_info (): name (NULL), file_time (), lines (), - coverage (), functions (NULL) +source_info::source_info (): name (NULL), file_time (), + lines (), coverage (), functions () { } +vector +source_info::get_functions_at_location (unsigned line_num) const +{ + vector r; + + for (vector::const_iterator it = functions.begin (); + it != functions.end (); it++) + { + if ((*it)->start_line == line_num && (*it)->src == index) + r.push_back (*it); + } + + return r; +} + class name_map { public: @@ -491,8 +529,9 @@ extern int main (int, char **); function_info::function_info (): name (NULL), demangled_name (NULL), ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0), + artificial (0), is_group (0), blocks (), blocks_executed (0), counts (NULL), num_counts (0), - line (0), src (0), next_file_fn (NULL), next (NULL) + start_line (0), end_line (0), src (0), lines (), next (NULL) { } @@ -514,6 +553,11 @@ function_info::~function_info () free (name); } +bool function_info::group_line_p (unsigned n, unsigned src_idx) +{ + return is_group && src == src_idx && start_line <= n && n <= end_line; +} + /* 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 @@ -880,6 +924,42 @@ process_args (int argc, char **argv) return optind; } +/* Output intermediate LINE sitting on LINE_NUM to output file F. */ + +static void +output_intermediate_line (FILE *f, line_info *line, unsigned line_num) +{ + if (!line->exists) + return; + + fprintf (f, "lcount:%u,%s,%d\n", line_num, + format_gcov (line->count, 0, -1), + line->has_unexecuted_block); + + vector::const_iterator it; + if (flag_branches) + for (it = line->branches.begin (); it != line->branches.end (); + it++) + { + if (!(*it)->is_unconditional && !(*it)->is_call_non_return) + { + const char *branch_type; + /* branch:, + branch_coverage_type + : notexec (Branch not executed) + : taken (Branch executed and taken) + : nottaken (Branch executed, but not taken) + */ + if ((*it)->src->count) + branch_type + = ((*it)->count > 0) ? "taken" : "nottaken"; + else + branch_type = "notexec"; + fprintf (f, "branch:%d,%s\n", line_num, branch_type); + } + } +} + /* Output the result in intermediate format used by 'lcov'. The intermediate format contains a single file named 'foo.cc.gcov', @@ -893,50 +973,94 @@ file 'foo.cc.gcov' similar to the above example. */ static void output_intermediate_file (FILE *gcov_file, source_info *src) { - unsigned line_num; /* current line number. */ - const line_info *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) + for (vector::iterator it = src->functions.begin (); + it != src->functions.end (); it++) { /* function:,, */ - 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); + fprintf (gcov_file, "function:%d,%s,%s\n", (*it)->start_line, + format_gcov ((*it)->blocks[0].count, 0, -1), + flag_demangled_names ? (*it)->demangled_name : (*it)->name); } - for (line_num = 1, line = &src->lines[line_num]; - line_num < src->lines.size (); - line_num++, line++) + for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++) { - if (line->exists) - fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num, - format_gcov (line->count, 0, -1), line->has_unexecuted_block); - if (flag_branches) - for (vector::const_iterator it = line->branches.begin (); - it != line->branches.end (); it++) - { - if (!(*it)->is_unconditional && !(*it)->is_call_non_return) - { - const char *branch_type; - /* branch:, - branch_coverage_type - : notexec (Branch not executed) - : taken (Branch executed and taken) - : nottaken (Branch executed, but not taken) - */ - if ((*it)->src->count) - branch_type = ((*it)->count > 0) ? "taken" : "nottaken"; - else - branch_type = "notexec"; - fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type); - } - } + vector fns = src->get_functions_at_location (line_num); + + /* Print first group functions that begin on the line. */ + for (vector::iterator it2 = fns.begin (); + it2 != fns.end (); it2++) + { + vector &lines = (*it2)->lines; + /* Print all lines covered by the function. */ + for (unsigned i = 0; i < lines.size (); i++) + { + line_info *line = &lines[i]; + output_intermediate_line (gcov_file, line, line_num + i); + } + } + + /* Follow with lines associated with the source file. */ + output_intermediate_line (gcov_file, &src->lines[line_num], line_num); } } +/* Function start pair. */ +struct function_start +{ + unsigned source_file_idx; + unsigned start_line; +}; + +/* Traits class for function start hash maps below. */ + +struct function_start_pair_hash : typed_noop_remove +{ + typedef function_start value_type; + typedef function_start compare_type; + + static hashval_t + hash (const function_start &ref) + { + inchash::hash hstate (0); + hstate.add_int (ref.source_file_idx); + hstate.add_int (ref.start_line); + return hstate.end (); + } + + static bool + equal (const function_start &ref1, const function_start &ref2) + { + return (ref1.source_file_idx == ref2.source_file_idx + && ref1.start_line == ref2.start_line); + } + + static void + mark_deleted (function_start &ref) + { + ref.start_line = ~1U; + } + + static void + mark_empty (function_start &ref) + { + ref.start_line = ~2U; + } + + static bool + is_deleted (const function_start &ref) + { + return ref.start_line == ~1U; + } + + static bool + is_empty (const function_start &ref) + { + return ref.start_line == ~2U; + } +}; + /* Process a single input file. */ static void @@ -949,6 +1073,27 @@ process_file (const char *file_name) if (!fns) return; + hash_map fn_map; + + /* Identify group functions. */ + for (function_t *f = fns; f; f = f->next) + if (!f->artificial) + { + function_start needle; + needle.source_file_idx = f->src; + needle.start_line = f->start_line; + + function_t **slot = fn_map.get (needle); + if (slot) + { + gcc_assert ((*slot)->end_line == f->end_line); + (*slot)->is_group = 1; + f->is_group = 1; + } + else + fn_map.put (needle, f); + } + read_count_file (fns); while (fns) { @@ -959,46 +1104,61 @@ process_file (const char *file_name) 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->blocks.size (); block_no++) + /* Process only non-artificial functions. */ + if (!fn->artificial) { - block_t *block = &fn->blocks[block_no]; - for (unsigned i = 0; i < block->locations.size (); i++) - { - unsigned s = block->locations[i].source_file_idx; + source_info *s = &sources[src]; + s->functions.push_back (fn); - /* Sort lines of locations. */ - sort (block->locations[i].lines.begin (), - block->locations[i].lines.end ()); - - if (!block->locations[i].lines.empty ()) + /* Mark last line in files touched by function. */ + for (block_no = 0; block_no != fn->blocks.size (); block_no++) + { + block_t *block = &fn->blocks[block_no]; + for (unsigned i = 0; i < block->locations.size (); i++) { - unsigned last_line - = block->locations[i].lines.back () + 1; - if (last_line > sources[s].lines.size ()) - sources[s].lines.resize (last_line); + /* Sort lines of locations. */ + sort (block->locations[i].lines.begin (), + block->locations[i].lines.end ()); + + if (!block->locations[i].lines.empty ()) + { + s = &sources[block->locations[i].source_file_idx]; + unsigned last_line + = block->locations[i].lines.back (); + + /* Record new lines for the function. */ + if (last_line >= s->lines.size ()) + { + /* Record new lines for a source file. */ + s->lines.resize (last_line + 1); + } + + /* It's possible that a function has some inlined + lines that are out of scope of the function. */ + for (int j = block->locations[i].lines.size () - 1; + j >= 0; j--) + { + unsigned ln = block->locations[i].lines[j]; + if (fn->group_line_p (ln, s->index)) + { + unsigned size = ln - fn->start_line + 1; + if (fn->lines.size () < size) + fn->lines.resize (size); + + break; + } + } + } } } + + solve_flow_graph (fn); + if (fn->has_catch) + find_exception_blocks (fn); } - solve_flow_graph (fn); - if (fn->has_catch) - find_exception_blocks (fn); *fn_end = fn; fn_end = &fn->next; } @@ -1048,6 +1208,8 @@ generate_results (const char *file_name) for (fn = functions; fn; fn = fn->next) { coverage_t coverage; + if (fn->artificial) + continue; memset (&coverage, 0, sizeof (coverage)); coverage.name = flag_demangled_names ? fn->demangled_name : fn->name; @@ -1228,6 +1390,7 @@ find_source (const char *file_name) src = &sources.back (); src->name = canon; src->coverage.name = src->name; + src->index = idx; if (source_length #if HAVE_DOS_BASED_FILE_SYSTEM /* You lose if separators don't match exactly in the @@ -1319,15 +1482,17 @@ read_graph_file (void) if (tag == GCOV_TAG_FUNCTION) { char *function_name; - unsigned ident, lineno; + unsigned ident; 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 ()); + unsigned artificial = gcov_read_unsigned (); unsigned src_idx = find_source (gcov_read_string ()); - lineno = gcov_read_unsigned (); + unsigned start_line = gcov_read_unsigned (); + unsigned end_line = gcov_read_unsigned (); fn = new function_t; fn->name = function_name; @@ -1341,9 +1506,10 @@ read_graph_file (void) fn->lineno_checksum = lineno_checksum; fn->cfg_checksum = cfg_checksum; fn->src = src_idx; - fn->line = lineno; + fn->start_line = start_line; + fn->end_line = end_line; + fn->artificial = artificial; - fn->next_file_fn = NULL; fn->next = NULL; *fns_end = fn; fns_end = &fn->next; @@ -2230,48 +2396,70 @@ add_line_counts (coverage_t *coverage, function_t *fn) fn->blocks_executed++; for (unsigned i = 0; i < block->locations.size (); i++) { - source_info *src = &sources[block->locations[i].source_file_idx]; - + unsigned src_idx = block->locations[i].source_file_idx; vector &lines = block->locations[i].lines; + + block->cycle.arc = NULL; + block->cycle.ident = ~0U; + for (unsigned j = 0; j < lines.size (); j++) { - line = &src->lines[lines[j]]; - if (coverage) + unsigned ln = lines[j]; + + /* Line belongs to a function that is in a group. */ + if (fn->group_line_p (ln, src_idx)) { - if (!line->exists) - coverage->lines++; - if (!line->count && block->count) - coverage->lines_executed++; + gcc_assert (lines[j] - fn->start_line < fn->lines.size ()); + line = &(fn->lines[lines[j] - fn->start_line]); + line->exists = 1; + if (!block->exceptional) + { + line->unexceptional = 1; + if (block->count == 0) + line->has_unexecuted_block = 1; + } + line->count += block->count; } - line->exists = 1; - if (!block->exceptional) + else { - line->unexceptional = 1; - if (block->count == 0) - line->has_unexecuted_block = 1; + gcc_assert (ln < sources[src_idx].lines.size ()); + line = &(sources[src_idx].lines[ln]); + if (coverage) + { + if (!line->exists) + coverage->lines++; + if (!line->count && block->count) + coverage->lines_executed++; + } + line->exists = 1; + if (!block->exceptional) + { + line->unexceptional = 1; + if (block->count == 0) + line->has_unexecuted_block = 1; + } + line->count += block->count; } - line->count += block->count; } - } - block->cycle.arc = NULL; - block->cycle.ident = ~0U; - has_any_line = true; - if (!ix || ix + 1 == fn->blocks.size ()) - /* Entry or exit block */; - else if (line != NULL) - { - line->blocks.push_back (block); + has_any_line = true; - if (flag_branches) + if (!ix || ix + 1 == fn->blocks.size ()) + /* Entry or exit block. */; + else if (line != NULL) { - arc_t *arc; + line->blocks.push_back (block); - for (arc = block->succ; arc; arc = arc->succ_next) + if (flag_branches) { - line->branches.push_back (arc); - if (coverage && !arc->is_unconditional) - add_branch_counts (coverage, arc); + arc_t *arc; + + for (arc = block->succ; arc; arc = arc->succ_next) + { + line->branches.push_back (arc); + if (coverage && !arc->is_unconditional) + add_branch_counts (coverage, arc); + } } } } @@ -2281,72 +2469,105 @@ add_line_counts (coverage_t *coverage, function_t *fn) fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); } -/* Accumulate the line counts of a file. */ +/* Accumulate info for LINE that belongs to SRC source file. If ADD_COVERAGE + is set to true, update source file summary. */ -static void -accumulate_line_counts (source_info *src) +static void accumulate_line_info (line_info *line, source_info *src, + bool add_coverage) { - function_t *fn, *fn_p, *fn_n; - unsigned ix = 0; + if (!line->blocks.empty ()) + { + /* 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. */ + gcov_type count = 0; + + /* Sum the entry arcs. */ + for (vector::iterator it = line->blocks.begin (); + it != line->blocks.end (); it++) + { + arc_t *arc; - /* Reverse the function order. */ - for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n) + for (arc = (*it)->pred; arc; arc = arc->pred_next) + if (flag_branches && add_coverage) + add_branch_counts (&src->coverage, arc); + } + + /* Cycle detection. */ + for (vector::iterator it = line->blocks.begin (); + it != line->blocks.end (); it++) + { + for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next) + if (!line->has_block (arc->src)) + count += arc->count; + for (arc_t *arc = (*it)->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 && add_coverage) { - fn_n = fn->next_file_fn; - fn->next_file_fn = fn_p; + src->coverage.lines++; + if (line->count) + src->coverage.lines_executed++; } - src->functions = fn_p; +} - for (vector::reverse_iterator it = src->lines.rbegin (); - it != src->lines.rend (); it++) +/* Accumulate the line counts of a file. */ + +static void +accumulate_line_counts (source_info *src) +{ + /* First work on group functions. */ + for (vector::iterator it = src->functions.begin (); + it != src->functions.end (); it++) { - line_info *line = &(*it); - if (!line->blocks.empty ()) - { - /* 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. */ - gcov_type count = 0; - - /* Sum the entry arcs. */ - for (vector::iterator it = line->blocks.begin (); - it != line->blocks.end (); it++) - { - arc_t *arc; + function_info *fn = *it; - for (arc = (*it)->pred; arc; arc = arc->pred_next) - if (flag_branches) - add_branch_counts (&src->coverage, arc); - } + if (fn->src != src->index || !fn->is_group) + continue; - /* Cycle detection. */ - for (vector::iterator it = line->blocks.begin (); - it != line->blocks.end (); it++) - { - for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next) - if (!line->has_block (arc->src)) - count += arc->count; - for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next) - arc->cs_count = arc->count; - } + for (vector::iterator it2 = fn->lines.begin (); + it2 != fn->lines.end (); it2++) + { + line_info *line = &(*it2); + accumulate_line_info (line, src, false); + } + } - /* Now, add the count of loops entirely on this line. */ - count += get_cycles_count (*line); - line->count = count; - } + /* Work on global lines that line in source file SRC. */ + for (vector::iterator it = src->lines.begin (); + it != src->lines.end (); it++) + accumulate_line_info (&(*it), src, true); - if (line->exists) - { - src->coverage.lines++; - if (line->count) - src->coverage.lines_executed++; - } + /* If not using intermediate mode, sum lines of group functions and + add them to lines that live in a source file. */ + if (!flag_intermediate_format) + for (vector::iterator it = src->functions.begin (); + it != src->functions.end (); it++) + { + function_info *fn = *it; - ix++; - } + if (fn->src != src->index || !fn->is_group) + continue; + + for (unsigned i = 0; i < fn->lines.size (); i++) + { + line_info &line = fn->lines[i]; + if (line.exists) + { + src->lines[fn->start_line + i].count += line.count; + src->lines[fn->start_line + i].exists = 1; + } + } + } } /* Output information about ARC number IX. Returns nonzero if @@ -2464,7 +2685,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional, if (flag_use_colors) { pad_count_string (s); - s = SGR_SEQ (COLOR_BG_MAGENTA COLOR_SEPARATOR COLOR_FG_WHITE); + s.insert (0, SGR_SEQ (COLOR_BG_MAGENTA + COLOR_SEPARATOR COLOR_FG_WHITE)); s += SGR_RESET; } else @@ -2502,6 +2724,86 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional, fprintf (f, "%s:%5u", s.c_str (), line_num); } +static void +print_source_line (FILE *f, const vector &source_lines, + unsigned line) +{ + gcc_assert (line >= 1); + gcc_assert (line <= source_lines.size ()); + + fprintf (f, ":%s\n", source_lines[line - 1]); +} + +/* Output line details for LINE and print it to F file. LINE lives on + LINE_NUM. */ + +static void +output_line_details (FILE *f, const line_info *line, unsigned line_num) +{ + if (flag_all_blocks) + { + arc_t *arc; + int ix, jx; + + ix = jx = 0; + for (vector::const_iterator it = line->blocks.begin (); + it != line->blocks.end (); it++) + { + if (!(*it)->is_call_return) + { + output_line_beginning (f, line->exists, + (*it)->exceptional, false, + (*it)->count, line_num, + "%%%%%", "$$$$$"); + fprintf (f, "-block %2d", ix++); + if (flag_verbose) + fprintf (f, " (BB %u)", (*it)->id); + fprintf (f, "\n"); + } + if (flag_branches) + for (arc = (*it)->succ; arc; arc = arc->succ_next) + jx += output_branch_count (f, jx, arc); + } + } + else if (flag_branches) + { + int ix; + + ix = 0; + for (vector::const_iterator it = line->branches.begin (); + it != line->branches.end (); it++) + ix += output_branch_count (f, ix, (*it)); + } +} + +/* Output detail statistics about function FN to file F. */ + +static void +output_function_details (FILE *f, const function_info *fn) +{ + if (!flag_branches) + return; + + 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 (f, "function %s", + flag_demangled_names ? fn->demangled_name : fn->name); + fprintf (f, " called %s", + format_gcov (called_count, 0, -1)); + fprintf (f, " returned %s", + format_gcov (return_count, called_count, 0)); + fprintf (f, " blocks executed %s", + format_gcov (fn->blocks_executed, fn->blocks.size () - 2, + 0)); + fprintf (f, "\n"); +} + /* 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. */ @@ -2512,10 +2814,7 @@ output_lines (FILE *gcov_file, const source_info *src) #define DEFAULT_LINE_START " -: 0:" FILE *source_file; - unsigned line_num; /* current line number. */ - const line_info *line; /* current line info ptr. */ const char *retval = ""; /* status of source file reading. */ - function_t *fn = NULL; fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name); if (!multiple_files) @@ -2536,36 +2835,35 @@ output_lines (FILE *gcov_file, const source_info *src) else if (src->file_time == 0) fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n"); - if (flag_branches) - fn = src->functions; + vector source_lines; + while ((retval = read_line (source_file)) != NULL) + source_lines.push_back (xstrdup (retval)); - for (line_num = 1, line = &src->lines[line_num]; - line_num < src->lines.size (); line_num++, line++) + unsigned line_start_group = 0; + vector fns; + + for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++) { - for (; fn && fn->line == line_num; fn = fn->next_file_fn) + if (line_num >= src->lines.size ()) { - 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->blocks.size () - 2, - 0)); - fprintf (gcov_file, "\n"); + fprintf (gcov_file, "%9s:%5u", "-", line_num); + print_source_line (gcov_file, source_lines, line_num); + continue; } - if (retval) - retval = read_line (source_file); + const line_info *line = &src->lines[line_num]; + + if (line_start_group == 0) + { + fns = src->get_functions_at_location (line_num); + if (fns.size () > 1) + line_start_group = fns[0]->end_line; + else if (fns.size () == 1) + { + function_t *fn = fns[0]; + output_function_details (gcov_file, fn); + } + } /* For lines which don't exist in the .bb file, print '-' before the source line. For lines which exist but were never @@ -2574,54 +2872,55 @@ output_lines (FILE *gcov_file, const source_info *src) There are 16 spaces of indentation added before the source line so that tabs won't be messed up. */ output_line_beginning (gcov_file, line->exists, line->unexceptional, - line->has_unexecuted_block, line->count, line_num, - "=====", "#####"); - fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/"); + line->has_unexecuted_block, line->count, + line_num, "=====", "#####"); - if (flag_all_blocks) - { - arc_t *arc; - int ix, jx; + print_source_line (gcov_file, source_lines, line_num); + output_line_details (gcov_file, line, line_num); - ix = jx = 0; - for (vector::const_iterator it = line->blocks.begin (); - it != line->blocks.end (); it++) + if (line_start_group == line_num) + { + for (vector::iterator it = fns.begin (); + it != fns.end (); it++) { - if (!(*it)->is_call_return) + function_info *fn = *it; + vector &lines = fn->lines; + + fprintf (gcov_file, "------------------\n"); + fprintf (gcov_file, "%s:\n", flag_demangled_names + ? fn->demangled_name : fn->name); + + output_function_details (gcov_file, fn); + + /* Print all lines covered by the function. */ + for (unsigned i = 0; i < lines.size (); i++) { + line_info *line = &lines[i]; + unsigned l = fn->start_line + i; + + /* 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. */ output_line_beginning (gcov_file, line->exists, - (*it)->exceptional, false, - (*it)->count, line_num, - "%%%%%", "$$$$$"); - fprintf (gcov_file, "-block %2d", ix++); - if (flag_verbose) - fprintf (gcov_file, " (BB %u)", (*it)->id); - fprintf (gcov_file, "\n"); + line->unexceptional, + line->has_unexecuted_block, + line->count, + l, "=====", "#####"); + + print_source_line (gcov_file, source_lines, l); + output_line_details (gcov_file, line, l); } - if (flag_branches) - for (arc = (*it)->succ; arc; arc = arc->succ_next) - jx += output_branch_count (gcov_file, jx, arc); } - } - else if (flag_branches) - { - int ix; - ix = 0; - for (vector::const_iterator it = line->branches.begin (); - it != line->branches.end (); it++) - ix += output_branch_count (gcov_file, ix, (*it)); + fprintf (gcov_file, "------------------\n"); + line_start_group = 0; } } - /* 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); } -- 2.14.3 --------------9DA2E967627CD5420CA13222--