Commit c8fda30f by Martin Liska Committed by Martin Liska

GCOV: introduce --json-format.

2018-10-29  Martin Liska  <mliska@suse.cz>

	* Makefile.in: Make dependency to json.o.
	* doc/gcov.texi: Document new JSON format, remove
	old intermediate format documentation.
	* gcov.c (struct function_info): Come up with m_name and
	m_demangled_name.
	(function_info::function_info): Initialize it.
	(function_info::~function_info): Release it.
	(main): Rename flag_intermediate_format to flag_json_format.
	(print_usage): Describe --json-format.
	(process_args): Set flag_json_format.
	(output_intermediate_line): Remove.
	(output_intermediate_json_line): Likewise.
	(get_gcov_intermediate_filename): Return new extension
	".gcov.json.gz".
	(output_intermediate_file): Implement JSON emission.
	(output_json_intermediate_file): Implement JSON emission.
	(generate_results): Use ::get_name for function name.
	Handle JSON output file.
	(read_graph_file): Use ::get_name instead of cplus_demangle.
	(read_count_file): Likewise.
	(solve_flow_graph): Likewise.
	(add_line_counts): Likewise.
	(accumulate_line_counts): Use new flag_json_format.
	(output_function_details): Use ::get_name instead of cplus_demangle.
	(output_lines): Likewise.
	* json.cc (test_writing_literals): Add new tests.
	* json.h (class literal): Add new boolean constructor.
2018-10-29  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/gcov-8.C: Do not check intermediate format.
	* lib/gcov.exp: Remove legacy verify-intermediate.

From-SVN: r265587
parent d4eb0305
2018-10-29 Martin Liska <mliska@suse.cz>
* Makefile.in: Make dependency to json.o.
* doc/gcov.texi: Document new JSON format, remove
old intermediate format documentation.
* gcov.c (struct function_info): Come up with m_name and
m_demangled_name.
(function_info::function_info): Initialize it.
(function_info::~function_info): Release it.
(main): Rename flag_intermediate_format to flag_json_format.
(print_usage): Describe --json-format.
(process_args): Set flag_json_format.
(output_intermediate_line): Remove.
(output_intermediate_json_line): Likewise.
(get_gcov_intermediate_filename): Return new extension
".gcov.json.gz".
(output_intermediate_file): Implement JSON emission.
(output_json_intermediate_file): Implement JSON emission.
(generate_results): Use ::get_name for function name.
Handle JSON output file.
(read_graph_file): Use ::get_name instead of cplus_demangle.
(read_count_file): Likewise.
(solve_flow_graph): Likewise.
(add_line_counts): Likewise.
(accumulate_line_counts): Use new flag_json_format.
(output_function_details): Use ::get_name instead of cplus_demangle.
(output_lines): Likewise.
* json.cc (test_writing_literals): Add new tests.
* json.h (class literal): Add new boolean constructor.
2018-10-29 Segher Boessenkool <segher@kernel.crashing.org> 2018-10-29 Segher Boessenkool <segher@kernel.crashing.org>
PR rtl-optimization/87701 PR rtl-optimization/87701
...@@ -2899,10 +2899,13 @@ s-iov: build/gcov-iov$(build_exeext) $(BASEVER) $(DEVPHASE) ...@@ -2899,10 +2899,13 @@ s-iov: build/gcov-iov$(build_exeext) $(BASEVER) $(DEVPHASE)
$(SHELL) $(srcdir)/../move-if-change tmp-gcov-iov.h gcov-iov.h $(SHELL) $(srcdir)/../move-if-change tmp-gcov-iov.h gcov-iov.h
$(STAMP) s-iov $(STAMP) s-iov
GCOV_OBJS = gcov.o # gcov.o needs $(ZLIBINC) added to the include flags.
CFLAGS-gcov.o += $(ZLIBINC)
GCOV_OBJS = gcov.o json.o
gcov$(exeext): $(GCOV_OBJS) $(LIBDEPS) gcov$(exeext): $(GCOV_OBJS) $(LIBDEPS)
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_OBJS) \ +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_OBJS) \
hash-table.o ggc-none.o $(LIBS) -o $@ hash-table.o ggc-none.o $(LIBS) $(ZLIB) -o $@
GCOV_DUMP_OBJS = gcov-dump.o GCOV_DUMP_OBJS = gcov-dump.o
gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
......
...@@ -124,7 +124,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}] ...@@ -124,7 +124,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
[@option{-c}|@option{--branch-counts}] [@option{-c}|@option{--branch-counts}]
[@option{-d}|@option{--display-progress}] [@option{-d}|@option{--display-progress}]
[@option{-f}|@option{--function-summaries}] [@option{-f}|@option{--function-summaries}]
[@option{-i}|@option{--intermediate-format}] [@option{-i}|@option{--json-format}]
[@option{-j}|@option{--human-readable}] [@option{-j}|@option{--human-readable}]
[@option{-k}|@option{--use-colors}] [@option{-k}|@option{--use-colors}]
[@option{-l}|@option{--long-file-names}] [@option{-l}|@option{--long-file-names}]
...@@ -181,79 +181,142 @@ Display help about using @command{gcov} (on the standard output), and ...@@ -181,79 +181,142 @@ Display help about using @command{gcov} (on the standard output), and
exit without doing any further processing. exit without doing any further processing.
@item -i @item -i
@itemx --intermediate-format @itemx --json-format
Output gcov file in an easy-to-parse intermediate text format that can Output gcov file in an easy-to-parse JSON intermediate format
be used by @command{lcov} or other tools. The output is a single which does not require source code for generation. The JSON
@file{.gcov} file per @file{.gcda} file. No source code is required. file is compressed with gzip compression algorithm
and the files have @file{.gcov.json.gz} extension.
The format of the intermediate @file{.gcov} file is plain text with Structure of the JSON is following:
one entry per line
@smallexample @smallexample
version:@var{gcc_version} @{
cwd:@var{working_directory} "current_working_directory": @var{current_working_directory},
file:@var{source_file_name} "format_version": @var{format_version},
function:@var{start_line_number},@var{end_line_number},@var{execution_count},@var{function_name} "gcc_version": @var{gcc_version}
lcount:@var{line number},@var{execution_count},@var{has_unexecuted_block} "files": [@var{file}]
branch:@var{line_number},@var{branch_coverage_type} @}
Where the @var{branch_coverage_type} is
notexec (Branch not executed)
taken (Branch executed and taken)
nottaken (Branch executed, but not taken)
@end smallexample @end smallexample
There can be multiple @var{file} entries in an intermediate gcov Fields of the root element have following semantics:
file. All entries following a @var{file} pertain to that source file
until the next @var{file} entry. If there are multiple functions that
start on a single line, then corresponding lcount is repeated multiple
times.
Here is a sample when @option{-i} is used in conjunction with @option{-b} option: @itemize @bullet
@item
@var{current_working_directory}: working directory where
a compilation unit was compiled
@item
@var{format_version}: semantic version of the format
@item
@var{gcc_version}: version of the GCC compiler
@end itemize
Each @var{file} has the following form:
@smallexample
@{
"file": @var{file_name},
"functions": [@var{function}],
"lines": [@var{line}]
@}
@end smallexample
Fields of the @var{file} element have following semantics:
@itemize @bullet
@item
@var{file_name}: name of the source file
@end itemize
Each @var{function} has the following form:
@smallexample
@{
"blocks": @var{blocks},
"blocks_executed": @var{blocks_executed},
"demangled_name": "@var{demangled_name},
"end_line": @var{end_line},
"execution_count": @var{execution_count},
"name": @var{name},
"start_line": @var{start_line}
@}
@end smallexample
Fields of the @var{function} element have following semantics:
@itemize @bullet
@item
@var{blocks}: number of blocks that are in the function
@item
@var{blocks_executed}: number of executed blocks of the function
@item
@var{demangled_name}: demangled name of the function
@item
@var{end_line}: line in the source file where the function ends
@item
@var{execution_count}: number of executions of the function
@item
@var{name}: name of the function
@item
@var{start_line}: line in the source file where the function begins
@end itemize
Each @var{line} has the following form:
@smallexample
@{
"branches": [@var{branch}],
"count": @var{count},
"line_number": @var{line_number},
"unexecuted_block": @var{unexecuted_block}
@}
@end smallexample
Branches are present only with @var{-b} option.
Fields of the @var{line} element have following semantics:
@itemize @bullet
@item
@var{count}: number of executions of the line
@item
@var{line_number}: line number
@item
@var{unexecuted_block}: flag whether the line contains an unexecuted block
(not all statements on the line are executed)
@end itemize
Each @var{branch} has the following form:
@smallexample @smallexample
version: 8.1.0 20180103 @{
cwd:/home/gcc/testcase "count": @var{count},
file:tmp.cpp "fallthrough": @var{fallthrough},
function:7,7,0,_ZN3FooIcEC2Ev "throw": @var{throw}
function:7,7,1,_ZN3FooIiEC2Ev @}
function:8,8,0,_ZN3FooIcE3incEv
function:8,8,2,_ZN3FooIiE3incEv
function:18,37,1,main
lcount:7,0,1
lcount:7,1,0
lcount:8,0,1
lcount:8,2,0
lcount:18,1,0
lcount:21,1,0
branch:21,taken
branch:21,nottaken
lcount:23,1,0
branch:23,taken
branch:23,nottaken
lcount:24,1,0
branch:24,taken
branch:24,nottaken
lcount:25,1,0
lcount:27,11,0
branch:27,taken
branch:27,taken
lcount:28,10,0
lcount:30,1,1
branch:30,nottaken
branch:30,taken
lcount:32,1,0
branch:32,nottaken
branch:32,taken
lcount:33,0,1
branch:33,notexec
branch:33,notexec
lcount:35,1,0
branch:35,taken
branch:35,nottaken
lcount:36,1,0
@end smallexample @end smallexample
Fields of the @var{branch} element have following semantics:
@itemize @bullet
@item
@var{count}: number of executions of the branch
@item
@var{fallthrough}: true when the branch is a fall through branch
@item
@var{throw}: true when the branch is an exceptional branch
@end itemize
@item -j @item -j
@itemx --human-readable @itemx --human-readable
Write counts in human readable format (like 24.6k). Write counts in human readable format (like 24.6k).
...@@ -842,7 +905,7 @@ some summary information. ...@@ -842,7 +905,7 @@ some summary information.
It is not recommended to access the coverage files directly. It is not recommended to access the coverage files directly.
Consumers should use the intermediate format that is provided Consumers should use the intermediate format that is provided
by @command{gcov} tool via @option{--intermediate-format} option. by @command{gcov} tool via @option{--json-format} option.
@node Cross-profiling @node Cross-profiling
@section Data File Relocation to Support Cross-Profiling @section Data File Relocation to Support Cross-Profiling
......
...@@ -44,7 +44,10 @@ along with Gcov; see the file COPYING3. If not see ...@@ -44,7 +44,10 @@ along with Gcov; see the file COPYING3. If not see
#include "version.h" #include "version.h"
#include "demangle.h" #include "demangle.h"
#include "color-macros.h" #include "color-macros.h"
#include "pretty-print.h"
#include "json.h"
#include <zlib.h>
#include <getopt.h> #include <getopt.h>
#include "md5.h" #include "md5.h"
...@@ -221,6 +224,10 @@ line_info::has_block (block_info *needle) ...@@ -221,6 +224,10 @@ line_info::has_block (block_info *needle)
return std::find (blocks.begin (), blocks.end (), needle) != blocks.end (); return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
} }
/* Output demangled function names. */
static int flag_demangled_names = 0;
/* Describes a single function. Contains an array of basic blocks. */ /* Describes a single function. Contains an array of basic blocks. */
struct function_info struct function_info
...@@ -241,8 +248,8 @@ struct function_info ...@@ -241,8 +248,8 @@ struct function_info
} }
/* Name of function. */ /* Name of function. */
char *name; char *m_name;
char *demangled_name; char *m_demangled_name;
unsigned ident; unsigned ident;
unsigned lineno_checksum; unsigned lineno_checksum;
unsigned cfg_checksum; unsigned cfg_checksum;
...@@ -284,6 +291,32 @@ struct function_info ...@@ -284,6 +291,32 @@ struct function_info
/* Next function. */ /* Next function. */
struct function_info *next; struct function_info *next;
/* Get demangled name of a function. The demangled name
is converted when it is used for the first time. */
char *get_demangled_name ()
{
if (m_demangled_name == NULL)
{
m_demangled_name = cplus_demangle (m_name, DMGL_PARAMS);
if (!m_demangled_name)
m_demangled_name = m_name;
}
return m_demangled_name;
}
/* Get name of the function based on flag_demangled_names. */
char *get_name ()
{
return flag_demangled_names ? get_demangled_name () : m_name;
}
/* Return number of basic blocks (without entry and exit block). */
unsigned get_block_count ()
{
return blocks.size () - 2;
}
}; };
/* Function info comparer that will sort functions according to starting /* Function info comparer that will sort functions according to starting
...@@ -477,13 +510,9 @@ static int flag_use_stdout = 0; ...@@ -477,13 +510,9 @@ static int flag_use_stdout = 0;
static int flag_display_progress = 0; static int flag_display_progress = 0;
/* Output *.gcov file in intermediate format used by 'lcov'. */ /* Output *.gcov file in JSON intermediate format used by consumers. */
static int flag_intermediate_format = 0; static int flag_json_format = 0;
/* Output demangled function names. */
static int flag_demangled_names = 0;
/* For included files, make the gcov output file name include the name /* 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, of the input source file. For example, if x.h is included in a.c,
...@@ -576,7 +605,7 @@ static char *mangle_name (const char *, char *); ...@@ -576,7 +605,7 @@ static char *mangle_name (const char *, char *);
static void release_structures (void); static void release_structures (void);
extern int main (int, char **); extern int main (int, char **);
function_info::function_info (): name (NULL), demangled_name (NULL), function_info::function_info (): m_name (NULL), m_demangled_name (NULL),
ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0), ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
artificial (0), is_group (0), artificial (0), is_group (0),
blocks (), blocks_executed (0), counts (), blocks (), blocks_executed (0), counts (),
...@@ -596,9 +625,9 @@ function_info::~function_info () ...@@ -596,9 +625,9 @@ function_info::~function_info ()
free (arc); free (arc);
} }
} }
if (flag_demangled_names && demangled_name != name) if (m_demangled_name != m_name)
free (demangled_name); free (m_demangled_name);
free (name); free (m_name);
} }
bool function_info::group_line_p (unsigned n, unsigned src_idx) bool function_info::group_line_p (unsigned n, unsigned src_idx)
...@@ -807,7 +836,7 @@ main (int argc, char **argv) ...@@ -807,7 +836,7 @@ main (int argc, char **argv)
argc - first_arg); argc - first_arg);
process_file (argv[argno]); process_file (argv[argno]);
if (flag_intermediate_format || argno == argc - 1) if (flag_json_format || argno == argc - 1)
{ {
process_all_functions (); process_all_functions ();
generate_results (argv[argno]); generate_results (argv[argno]);
...@@ -836,7 +865,7 @@ print_usage (int error_p) ...@@ -836,7 +865,7 @@ print_usage (int error_p)
fnotice (file, " -d, --display-progress Display progress information\n"); fnotice (file, " -d, --display-progress Display progress information\n");
fnotice (file, " -f, --function-summaries Output summaries for each function\n"); fnotice (file, " -f, --function-summaries Output summaries for each function\n");
fnotice (file, " -h, --help Print this help, then exit\n"); fnotice (file, " -h, --help Print this help, then exit\n");
fnotice (file, " -i, --intermediate-format Output .gcov file in intermediate text format\n"); fnotice (file, " -i, --json-format Output JSON intermediate format into .gcov.json.gz file\n");
fnotice (file, " -j, --human-readable Output human readable numbers\n"); fnotice (file, " -j, --human-readable Output human readable numbers\n");
fnotice (file, " -k, --use-colors Emit colored output\n"); fnotice (file, " -k, --use-colors Emit colored output\n");
fnotice (file, " -l, --long-file-names Use long output file names for included\n\ fnotice (file, " -l, --long-file-names Use long output file names for included\n\
...@@ -881,7 +910,7 @@ static const struct option options[] = ...@@ -881,7 +910,7 @@ static const struct option options[] =
{ "all-blocks", no_argument, NULL, 'a' }, { "all-blocks", no_argument, NULL, 'a' },
{ "branch-probabilities", no_argument, NULL, 'b' }, { "branch-probabilities", no_argument, NULL, 'b' },
{ "branch-counts", no_argument, NULL, 'c' }, { "branch-counts", no_argument, NULL, 'c' },
{ "intermediate-format", no_argument, NULL, 'i' }, { "json-format", no_argument, NULL, 'i' },
{ "human-readable", no_argument, NULL, 'j' }, { "human-readable", no_argument, NULL, 'j' },
{ "no-output", no_argument, NULL, 'n' }, { "no-output", no_argument, NULL, 'n' },
{ "long-file-names", no_argument, NULL, 'l' }, { "long-file-names", no_argument, NULL, 'l' },
...@@ -963,12 +992,12 @@ process_args (int argc, char **argv) ...@@ -963,12 +992,12 @@ process_args (int argc, char **argv)
flag_unconditional = 1; flag_unconditional = 1;
break; break;
case 'i': case 'i':
flag_intermediate_format = 1; flag_json_format = 1;
flag_gcov_file = 1; flag_gcov_file = 1;
break; break;
case 'd': case 'd':
flag_display_progress = 1; flag_display_progress = 1;
break; break;
case 'x': case 'x':
flag_hash_filenames = 1; flag_hash_filenames = 1;
break; break;
...@@ -990,17 +1019,23 @@ process_args (int argc, char **argv) ...@@ -990,17 +1019,23 @@ process_args (int argc, char **argv)
return optind; return optind;
} }
/* Output intermediate LINE sitting on LINE_NUM to output file F. */ /* Output intermediate LINE sitting on LINE_NUM to JSON OBJECT. */
static void static void
output_intermediate_line (FILE *f, line_info *line, unsigned line_num) output_intermediate_json_line (json::array *object,
line_info *line, unsigned line_num)
{ {
if (!line->exists) if (!line->exists)
return; return;
fprintf (f, "lcount:%u,%s,%d\n", line_num, json::object *lineo = new json::object ();
format_gcov (line->count, 0, -1), lineo->set ("line_number", new json::number (line_num));
line->has_unexecuted_block); lineo->set ("count", new json::number (line->count));
lineo->set ("unexecuted_block",
new json::literal (line->has_unexecuted_block));
json::array *branches = new json::array ();
lineo->set ("branches", branches);
vector<arc_info *>::const_iterator it; vector<arc_info *>::const_iterator it;
if (flag_branches) if (flag_branches)
...@@ -1009,21 +1044,16 @@ output_intermediate_line (FILE *f, line_info *line, unsigned line_num) ...@@ -1009,21 +1044,16 @@ output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
{ {
if (!(*it)->is_unconditional && !(*it)->is_call_non_return) if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
{ {
const char *branch_type; json::object *branch = new json::object ();
/* branch:<line_num>,<branch_coverage_infoype> branch->set ("count", new json::number ((*it)->count));
branch_coverage_infoype branch->set ("throw", new json::literal ((*it)->is_throw));
: notexec (Branch not executed) branch->set ("fallthrough",
: taken (Branch executed and taken) new json::literal ((*it)->fall_through));
: nottaken (Branch executed, but not taken) branches->append (branch);
*/
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);
} }
} }
object->append (lineo);
} }
/* Get the name of the gcov file. The return value must be free'd. /* Get the name of the gcov file. The return value must be free'd.
...@@ -1038,7 +1068,7 @@ output_intermediate_line (FILE *f, line_info *line, unsigned line_num) ...@@ -1038,7 +1068,7 @@ output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
static char * static char *
get_gcov_intermediate_filename (const char *file_name) get_gcov_intermediate_filename (const char *file_name)
{ {
const char *gcov = ".gcov"; const char *gcov = ".gcov.json.gz";
char *result; char *result;
const char *cptr; const char *cptr;
...@@ -1051,34 +1081,44 @@ get_gcov_intermediate_filename (const char *file_name) ...@@ -1051,34 +1081,44 @@ get_gcov_intermediate_filename (const char *file_name)
return result; return result;
} }
/* Output the result in intermediate format used by 'lcov'. /* Output the result in JSON intermediate format.
Source info SRC is dumped into JSON_FILES which is JSON array. */
The intermediate format contains a single file named 'foo.cc.gcov',
with no source code included.
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 static void
output_intermediate_file (FILE *gcov_file, source_info *src) output_json_intermediate_file (json::array *json_files, source_info *src)
{ {
fprintf (gcov_file, "version:%s\n", version_string); json::object *root = new json::object ();
fprintf (gcov_file, "file:%s\n", src->name); /* source file name */ json_files->append (root);
fprintf (gcov_file, "cwd:%s\n", bbg_cwd);
root->set ("file", new json::string (src->name));
json::array *functions = new json::array ();
root->set ("functions", functions);
std::sort (src->functions.begin (), src->functions.end (), std::sort (src->functions.begin (), src->functions.end (),
function_line_start_cmp ()); function_line_start_cmp ());
for (vector<function_info *>::iterator it = src->functions.begin (); for (vector<function_info *>::iterator it = src->functions.begin ();
it != src->functions.end (); it++) it != src->functions.end (); it++)
{ {
/* function:<name>,<line_number>,<execution_count> */ json::object *function = new json::object ();
fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line, function->set ("name", new json::string ((*it)->m_name));
(*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1), function->set ("demangled_name",
flag_demangled_names ? (*it)->demangled_name : (*it)->name); new json::string ((*it)->get_demangled_name ()));
function->set ("start_line", new json::number ((*it)->start_line));
function->set ("end_line", new json::number ((*it)->end_line));
function->set ("blocks",
new json::number ((*it)->get_block_count ()));
function->set ("blocks_executed",
new json::number ((*it)->blocks_executed));
function->set ("execution_count",
new json::number ((*it)->blocks[0].count));
functions->append (function);
} }
json::array *lineso = new json::array ();
root->set ("lines", lineso);
for (unsigned line_num = 1; line_num <= src->lines.size (); line_num++) for (unsigned line_num = 1; line_num <= src->lines.size (); line_num++)
{ {
vector<function_info *> fns = src->get_functions_at_location (line_num); vector<function_info *> fns = src->get_functions_at_location (line_num);
...@@ -1091,13 +1131,13 @@ output_intermediate_file (FILE *gcov_file, source_info *src) ...@@ -1091,13 +1131,13 @@ output_intermediate_file (FILE *gcov_file, source_info *src)
for (unsigned i = 0; i < lines.size (); i++) for (unsigned i = 0; i < lines.size (); i++)
{ {
line_info *line = &lines[i]; line_info *line = &lines[i];
output_intermediate_line (gcov_file, line, line_num + i); output_intermediate_json_line (lineso, line, line_num + i);
} }
} }
/* Follow with lines associated with the source file. */ /* Follow with lines associated with the source file. */
if (line_num < src->lines.size ()) if (line_num < src->lines.size ())
output_intermediate_line (gcov_file, &src->lines[line_num], line_num); output_intermediate_json_line (lineso, &src->lines[line_num], line_num);
} }
} }
...@@ -1301,8 +1341,7 @@ output_gcov_file (const char *file_name, source_info *src) ...@@ -1301,8 +1341,7 @@ output_gcov_file (const char *file_name, source_info *src)
static void static void
generate_results (const char *file_name) generate_results (const char *file_name)
{ {
FILE *gcov_intermediate_file = NULL; char *gcov_intermediate_filename;
char *gcov_intermediate_filename = NULL;
for (vector<function_info *>::iterator it = functions.begin (); for (vector<function_info *>::iterator it = functions.begin ();
it != functions.end (); it++) it != functions.end (); it++)
...@@ -1311,7 +1350,7 @@ generate_results (const char *file_name) ...@@ -1311,7 +1350,7 @@ generate_results (const char *file_name)
coverage_info coverage; coverage_info coverage;
memset (&coverage, 0, sizeof (coverage)); memset (&coverage, 0, sizeof (coverage));
coverage.name = flag_demangled_names ? fn->demangled_name : fn->name; coverage.name = fn->get_name ();
add_line_counts (flag_function_summary ? &coverage : NULL, fn); add_line_counts (flag_function_summary ? &coverage : NULL, fn);
if (flag_function_summary) if (flag_function_summary)
{ {
...@@ -1333,18 +1372,15 @@ generate_results (const char *file_name) ...@@ -1333,18 +1372,15 @@ generate_results (const char *file_name)
file_name = canonicalize_name (file_name); file_name = canonicalize_name (file_name);
} }
if (flag_gcov_file && flag_intermediate_format && !flag_use_stdout) gcov_intermediate_filename = get_gcov_intermediate_filename (file_name);
{
/* Open the intermediate file. */ json::object *root = new json::object ();
gcov_intermediate_filename = get_gcov_intermediate_filename (file_name); root->set ("format_version", new json::string ("1"));
gcov_intermediate_file = fopen (gcov_intermediate_filename, "w"); root->set ("gcc_version", new json::string (version_string));
if (!gcov_intermediate_file) root->set ("current_working_directory", new json::string (bbg_cwd));
{
fnotice (stderr, "Cannot open intermediate output file %s\n", json::array *json_files = new json::array ();
gcov_intermediate_filename); root->set ("files", json_files);
return;
}
}
for (vector<source_info>::iterator it = sources.begin (); for (vector<source_info>::iterator it = sources.begin ();
it != sources.end (); it++) it != sources.end (); it++)
...@@ -1372,11 +1408,8 @@ generate_results (const char *file_name) ...@@ -1372,11 +1408,8 @@ generate_results (const char *file_name)
total_executed += src->coverage.lines_executed; total_executed += src->coverage.lines_executed;
if (flag_gcov_file) if (flag_gcov_file)
{ {
if (flag_intermediate_format) if (flag_json_format)
/* Output the intermediate format without requiring source output_json_intermediate_file (json_files, src);
files. This outputs a section to a *single* file. */
output_intermediate_file ((flag_use_stdout
? stdout : gcov_intermediate_file), src);
else else
{ {
if (flag_use_stdout) if (flag_use_stdout)
...@@ -1393,11 +1426,35 @@ generate_results (const char *file_name) ...@@ -1393,11 +1426,35 @@ generate_results (const char *file_name)
} }
} }
if (flag_gcov_file && flag_intermediate_format && !flag_use_stdout) if (flag_gcov_file && flag_json_format)
{ {
/* Now we've finished writing the intermediate file. */ if (flag_use_stdout)
fclose (gcov_intermediate_file); {
XDELETEVEC (gcov_intermediate_filename); root->dump (stdout);
printf ("\n");
}
else
{
pretty_printer pp;
root->print (&pp);
pp_formatted_text (&pp);
gzFile output = gzopen (gcov_intermediate_filename, "w");
if (output == NULL)
{
fnotice (stderr, "Cannot open JSON output file %s\n",
gcov_intermediate_filename);
return;
}
if (gzputs (output, pp_formatted_text (&pp)) == EOF
|| gzclose (output))
{
fnotice (stderr, "Error writing JSON output file %s\n",
gcov_intermediate_filename);
return;
}
}
} }
if (!file_name) if (!file_name)
...@@ -1634,13 +1691,7 @@ read_graph_file (void) ...@@ -1634,13 +1691,7 @@ read_graph_file (void)
fn = new function_info (); fn = new function_info ();
functions.push_back (fn); functions.push_back (fn);
fn->name = function_name; fn->m_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->ident = ident;
fn->lineno_checksum = lineno_checksum; fn->lineno_checksum = lineno_checksum;
fn->cfg_checksum = cfg_checksum; fn->cfg_checksum = cfg_checksum;
...@@ -1656,7 +1707,7 @@ read_graph_file (void) ...@@ -1656,7 +1707,7 @@ read_graph_file (void)
{ {
if (!fn->blocks.empty ()) if (!fn->blocks.empty ())
fnotice (stderr, "%s:already seen blocks for '%s'\n", fnotice (stderr, "%s:already seen blocks for '%s'\n",
bbg_file_name, fn->name); bbg_file_name, fn->get_name ());
else else
fn->blocks.resize (gcov_read_unsigned ()); fn->blocks.resize (gcov_read_unsigned ());
} }
...@@ -1862,7 +1913,7 @@ read_count_file (void) ...@@ -1862,7 +1913,7 @@ read_count_file (void)
{ {
mismatch:; mismatch:;
fnotice (stderr, "%s:profile mismatch for '%s'\n", fnotice (stderr, "%s:profile mismatch for '%s'\n",
da_file_name, fn->name); da_file_name, fn->get_name ());
goto cleanup; goto cleanup;
} }
} }
...@@ -1927,12 +1978,12 @@ solve_flow_graph (function_info *fn) ...@@ -1927,12 +1978,12 @@ solve_flow_graph (function_info *fn)
if (fn->blocks.size () < 2) if (fn->blocks.size () < 2)
fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n", fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
bbg_file_name, fn->name); bbg_file_name, fn->get_name ());
else else
{ {
if (fn->blocks[ENTRY_BLOCK].num_pred) if (fn->blocks[ENTRY_BLOCK].num_pred)
fnotice (stderr, "%s:'%s' has arcs to entry block\n", fnotice (stderr, "%s:'%s' has arcs to entry block\n",
bbg_file_name, fn->name); bbg_file_name, fn->get_name ());
else else
/* We can't deduce the entry block counts from the lack of /* We can't deduce the entry block counts from the lack of
predecessors. */ predecessors. */
...@@ -1940,7 +1991,7 @@ solve_flow_graph (function_info *fn) ...@@ -1940,7 +1991,7 @@ solve_flow_graph (function_info *fn)
if (fn->blocks[EXIT_BLOCK].num_succ) if (fn->blocks[EXIT_BLOCK].num_succ)
fnotice (stderr, "%s:'%s' has arcs from exit block\n", fnotice (stderr, "%s:'%s' has arcs from exit block\n",
bbg_file_name, fn->name); bbg_file_name, fn->get_name ());
else else
/* Likewise, we can't deduce exit block counts from the lack /* Likewise, we can't deduce exit block counts from the lack
of its successors. */ of its successors. */
...@@ -2149,7 +2200,7 @@ solve_flow_graph (function_info *fn) ...@@ -2149,7 +2200,7 @@ solve_flow_graph (function_info *fn)
if (!fn->blocks[i].count_valid) if (!fn->blocks[i].count_valid)
{ {
fnotice (stderr, "%s:graph is unsolvable for '%s'\n", fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
bbg_file_name, fn->name); bbg_file_name, fn->get_name ());
break; break;
} }
} }
...@@ -2553,7 +2604,8 @@ add_line_counts (coverage_info *coverage, function_info *fn) ...@@ -2553,7 +2604,8 @@ add_line_counts (coverage_info *coverage, function_info *fn)
} }
if (!has_any_line) if (!has_any_line)
fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name,
fn->get_name ());
} }
/* Accumulate info for LINE that belongs to SRC source file. If ADD_COVERAGE /* Accumulate info for LINE that belongs to SRC source file. If ADD_COVERAGE
...@@ -2633,7 +2685,7 @@ accumulate_line_counts (source_info *src) ...@@ -2633,7 +2685,7 @@ accumulate_line_counts (source_info *src)
/* If not using intermediate mode, sum lines of group functions and /* If not using intermediate mode, sum lines of group functions and
add them to lines that live in a source file. */ add them to lines that live in a source file. */
if (!flag_intermediate_format) if (!flag_json_format)
for (vector<function_info *>::iterator it = src->functions.begin (); for (vector<function_info *>::iterator it = src->functions.begin ();
it != src->functions.end (); it++) it != src->functions.end (); it++)
{ {
...@@ -2895,7 +2947,7 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num) ...@@ -2895,7 +2947,7 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
/* Output detail statistics about function FN to file F. */ /* Output detail statistics about function FN to file F. */
static void static void
output_function_details (FILE *f, const function_info *fn) output_function_details (FILE *f, function_info *fn)
{ {
if (!flag_branches) if (!flag_branches)
return; return;
...@@ -2908,15 +2960,13 @@ output_function_details (FILE *f, const function_info *fn) ...@@ -2908,15 +2960,13 @@ output_function_details (FILE *f, const function_info *fn)
if (arc->fake) if (arc->fake)
return_count -= arc->count; return_count -= arc->count;
fprintf (f, "function %s", fprintf (f, "function %s", fn->get_name ());
flag_demangled_names ? fn->demangled_name : fn->name);
fprintf (f, " called %s", fprintf (f, " called %s",
format_gcov (called_count, 0, -1)); format_gcov (called_count, 0, -1));
fprintf (f, " returned %s", fprintf (f, " returned %s",
format_gcov (return_count, called_count, 0)); format_gcov (return_count, called_count, 0));
fprintf (f, " blocks executed %s", fprintf (f, " blocks executed %s",
format_gcov (fn->blocks_executed, fn->blocks.size () - 2, format_gcov (fn->blocks_executed, fn->get_block_count (), 0));
0));
fprintf (f, "\n"); fprintf (f, "\n");
} }
...@@ -3028,9 +3078,7 @@ output_lines (FILE *gcov_file, const source_info *src) ...@@ -3028,9 +3078,7 @@ output_lines (FILE *gcov_file, const source_info *src)
fprintf (gcov_file, FN_SEPARATOR); fprintf (gcov_file, FN_SEPARATOR);
string fn_name string fn_name = fn->get_name ();
= flag_demangled_names ? fn->demangled_name : fn->name;
if (flag_use_colors) if (flag_use_colors)
{ {
fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN)); fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
......
...@@ -296,6 +296,9 @@ test_writing_literals () ...@@ -296,6 +296,9 @@ test_writing_literals ()
assert_print_eq (literal (JSON_TRUE), "true"); assert_print_eq (literal (JSON_TRUE), "true");
assert_print_eq (literal (JSON_FALSE), "false"); assert_print_eq (literal (JSON_FALSE), "false");
assert_print_eq (literal (JSON_NULL), "null"); assert_print_eq (literal (JSON_NULL), "null");
assert_print_eq (literal (true), "true");
assert_print_eq (literal (false), "false");
} }
/* Run all of the selftests within this file. */ /* Run all of the selftests within this file. */
......
...@@ -154,6 +154,9 @@ class literal : public value ...@@ -154,6 +154,9 @@ class literal : public value
public: public:
literal (enum kind kind) : m_kind (kind) {} literal (enum kind kind) : m_kind (kind) {}
/* Construct literal for a boolean value. */
literal (bool value): m_kind (value ? JSON_TRUE : JSON_FALSE) {}
enum kind get_kind () const FINAL OVERRIDE { return m_kind; } enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
void print (pretty_printer *pp) const FINAL OVERRIDE; void print (pretty_printer *pp) const FINAL OVERRIDE;
......
2018-10-29 Martin Liska <mliska@suse.cz>
* g++.dg/gcov/gcov-8.C: Do not check intermediate format.
* lib/gcov.exp: Remove legacy verify-intermediate.
2018-10-28 Kugan Vivekanandarajah <kuganv@linaro.org> 2018-10-28 Kugan Vivekanandarajah <kuganv@linaro.org>
* gcc.dg/gimplefe-30.c: New test. * gcc.dg/gimplefe-30.c: New test.
......
/* Verify that intermediate coverage format can be generated for simple code. */
/* { dg-options "-fprofile-arcs -ftest-coverage" } */ /* { dg-options "-fprofile-arcs -ftest-coverage" } */
/* { dg-do run { target native } } */ /* { dg-do run { target native } } */
...@@ -32,4 +30,4 @@ int main() ...@@ -32,4 +30,4 @@ int main()
foo(); foo();
} }
/* { dg-final { run-gcov intermediate { -i -b gcov-8.C } } } */ /* { dg-final { run-gcov { -b gcov-8.C } } } */
...@@ -84,61 +84,6 @@ proc verify-lines { testname testcase file } { ...@@ -84,61 +84,6 @@ proc verify-lines { testname testcase file } {
# #
# verify-intermediate -- check that intermediate file has certain lines
#
# TESTNAME is the name of the test, including unique flags.
# TESTCASE is the name of the test.
# FILE is the name of the gcov output file.
#
# Checks are very loose, they are based on certain tags being present
# in the output. They do not check for exact expected execution
# counts. For that the regular gcov format should be checked.
#
proc verify-intermediate { testname testcase file } {
set failed 0
set srcfile 0
set function 0
set lcount 0
set branch 0
set fd [open $file r]
while { [gets $fd line] >= 0 } {
if [regexp "^file:" $line] {
incr srcfile
}
if [regexp "^function:(\[0-9\]+),(\[0-9\]+),.*" $line] {
incr function
}
if [regexp "^lcount:(\[0-9\]+),(\[0-9\]+),(\[01\])" $line] {
incr lcount
}
if [regexp "^branch:(\[0-9\]+),(taken|nottaken|notexec)" $line] {
incr branch
}
}
# We should see at least one tag of each type
if {$srcfile == 0} {
fail "$testname expected 'file:' tag not found"
incr failed
}
if {$function == 0} {
fail "$testname expected 'function:' tag not found"
incr failed
}
if {$lcount == 0} {
fail "$testname expected 'lcount:' tag not found"
incr failed
}
if {$branch == 0} {
fail "$testname expected 'branch:' tag not found"
incr failed
}
close $fd
return $failed
}
#
# verify-branches -- check that branch percentages are as expected # verify-branches -- check that branch percentages are as expected
# #
# TESTNAME is the name of the test, including unique flags. # TESTNAME is the name of the test, including unique flags.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment