gcov-tool: Add merge-stream subcommand

gcc/

	* doc/gcov-tool.texi: Document merge-stream subcommand.
	* doc/invoke.texi (fprofile-info-section): Mention merge-stream
	subcommand of gcov-tool.
	* gcov-tool.cc (gcov_profile_merge_stream): Declare.
	(print_merge_stream_usage_message): New.
	(merge_stream_usage): Likewise.
	(do_merge_stream): Likewise.
	(print_usage): Call print_merge_stream_usage_message().
	(main): Call do_merge_stream() to execute merge-stream subcommand.

libgcc/

	* libgcov-util.c (consume_stream): New.
	(get_target_profiles_for_merge): Likewise.
	(gcov_profile_merge_stream): Likewise.
This commit is contained in:
Sebastian Huber 2022-03-30 16:53:29 +02:00
parent ef9a53feae
commit 210e32b60b
4 changed files with 212 additions and 0 deletions

View File

@ -52,6 +52,10 @@ Current gcov-tool supports the following functionalities:
@item
merge two sets of profiles with weights.
@item
read a stream of profiles with associated filenames and merge it with a set of
profiles with weights.
@item
read one set of profile and rewrite profile contents. One can scale or
normalize the count values.
@ -64,6 +68,12 @@ Collect the profiles for different set of inputs, and use this tool to merge
them. One can specify the weight to factor in the relative importance of
each input.
@item
Collect profiles from target systems without a filesystem (freestanding
environments). Merge the collected profiles with associated profiles
present on the host system. One can specify the weight to factor in the
relative importance of each input.
@item
Rewrite the profile after removing a subset of the gcda files, while maintaining
the consistency of the summary and the histogram.
@ -117,6 +127,10 @@ gcov-tool merge [merge-options] @var{directory1} @var{directory2}
[@option{-v}|@option{--verbose}]
[@option{-w}|@option{--weight} @var{w1,w2}]
gcov-tool merge-stream [merge-stream-options] [@var{file}]
[@option{-v}|@option{--verbose}]
[@option{-w}|@option{--weight} @var{w1,w2}]
gcov-tool rewrite [rewrite-options] @var{directory}
[@option{-n}|@option{--normalize} @var{long_long_value}]
[@option{-o}|@option{--output} @var{directory}]
@ -169,6 +183,28 @@ Set the merge weights of the @var{directory1} and @var{directory2},
respectively. The default weights are 1 for both.
@end table
@item merge-stream
Collect profiles with associated filenames from a @emph{gcfn} and @emph{gcda}
data stream. Read the stream from the file specified by @var{file} or from
@file{stdin}. Merge the profiles with associated profiles in the host
filesystem. Apply the optional weights while merging profiles.
For the generation of a @emph{gcfn} and @emph{gcda} data stream on the target
system, please have a look at the @code{__gcov_filename_to_gcfn()} and
@code{__gcov_info_to_gcda()} functions declared in @code{#include <gcov.h>}.
@table @gcctabopt
@item -v
@itemx --verbose
Set the verbose mode.
@item -w @var{w1},@var{w2}
@itemx --weight @var{w1},@var{w2}
Set the merge weights of the profiles from the @emph{gcfn} and @emph{gcda} data
stream and the associated profiles in the host filesystem, respectively. The
default weights are 1 for both.
@end table
@item rewrite
Read the specified profile directory and rewrite to a new directory.
@table @gcctabopt

View File

@ -15537,6 +15537,11 @@ main (void)
@}
@end smallexample
The @command{merge-stream} subcommand of @command{gcov-tool} may be used to
deserialize the data stream generated by the @code{__gcov_filename_to_gcfn} and
@code{__gcov_info_to_gcda} functions and merge the profile information into
@file{.gcda} files on the host filesystem.
@item -fprofile-note=@var{path}
@opindex fprofile-note

View File

@ -42,6 +42,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
extern struct gcov_info *gcov_profile_merge (struct gcov_info*,
struct gcov_info*, int, int);
extern struct gcov_info *gcov_profile_merge_stream (const char *, int, int);
extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*);
extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
extern int gcov_profile_scale (struct gcov_info*, float, int, int);
@ -229,6 +230,78 @@ do_merge (int argc, char **argv)
return profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
}
/* Usage message for profile merge-stream. */
static void
print_merge_stream_usage_message (int error_p)
{
FILE *file = error_p ? stderr : stdout;
fnotice (file, " merge-stream [options] [<file>] Merge coverage stream file (or stdin)\n"
" and coverage file contents\n");
fnotice (file, " -v, --verbose Verbose mode\n");
fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
}
static const struct option merge_stream_options[] =
{
{ "verbose", no_argument, NULL, 'v' },
{ "weight", required_argument, NULL, 'w' },
{ 0, 0, 0, 0 }
};
/* Print merge-stream usage and exit. */
static void ATTRIBUTE_NORETURN
merge_stream_usage (void)
{
fnotice (stderr, "Merge-stream subcomand usage:");
print_merge_stream_usage_message (true);
exit (FATAL_EXIT_CODE);
}
/* Driver for profile merge-stream sub-command. */
static int
do_merge_stream (int argc, char **argv)
{
int opt;
int w1 = 1, w2 = 1;
struct gcov_info *merged_profile;
optind = 0;
while ((opt = getopt_long (argc, argv, "vw:",
merge_stream_options, NULL)) != -1)
{
switch (opt)
{
case 'v':
verbose = true;
gcov_set_verbose ();
break;
case 'w':
sscanf (optarg, "%d,%d", &w1, &w2);
if (w1 < 0 || w2 < 0)
fatal_error (input_location, "weights need to be non-negative");
break;
default:
merge_stream_usage ();
}
}
if (argc - optind > 1)
merge_stream_usage ();
merged_profile = gcov_profile_merge_stream (argv[optind], w1, w2);
if (merged_profile)
gcov_do_dump (merged_profile, 0, -1);
else if (verbose)
fnotice (stdout, "no profile files were merged\n");
return 0;
}
/* If N_VAL is no-zero, normalize the profile by setting the largest counter
counter value to N_VAL and scale others counters proportionally.
Otherwise, multiply the all counters by SCALE. */
@ -505,6 +578,7 @@ print_usage (int error_p)
fnotice (file, " -h, --help Print this help, then exit\n");
fnotice (file, " -v, --version Print version number, then exit\n");
print_merge_usage_message (error_p);
print_merge_stream_usage_message (error_p);
print_rewrite_usage_message (error_p);
print_overlap_usage_message (error_p);
fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
@ -594,6 +668,8 @@ main (int argc, char **argv)
if (!strcmp (sub_command, "merge"))
return do_merge (argc - optind, argv + optind);
else if (!strcmp (sub_command, "merge-stream"))
return do_merge_stream (argc - optind, argv + optind);
else if (!strcmp (sub_command, "rewrite"))
return do_rewrite (argc - optind, argv + optind);
else if (!strcmp (sub_command, "overlap"))

View File

@ -735,6 +735,101 @@ gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile
return tgt_profile;
}
/* Deserialize gcov_info objects and associated filenames from the file
specified by FILENAME to create a profile list. When FILENAME is NULL, read
from stdin. Return the profile list. */
struct gcov_info *
deserialize_profiles (const char *filename)
{
read_profile_dir_init ();
while (true)
{
unsigned version;
const char *filename_of_info;
struct gcov_info *obj_info;
if (!gcov_magic (gcov_read_unsigned (), GCOV_FILENAME_MAGIC))
{
if (gcov_is_error () != 2)
fnotice (stderr, "%s:not a gcfn stream\n", filename);
break;
}
version = gcov_read_unsigned ();
if (version != GCOV_VERSION)
{
fnotice (stderr, "%s:incorrect gcov version %d vs %d \n",
filename, version, GCOV_VERSION);
break;
}
filename_of_info = gcov_read_string ();
if (!filename_of_info)
{
fnotice (stderr, "%s:no filename in gcfn stream\n",
filename);
break;
}
obj_info = read_gcda_file (filename);
if (!obj_info)
break;
obj_info->filename = filename_of_info;
}
return gcov_info_head;
}
/* For each profile of the list specified by SRC_PROFILE, read the GCDA file of
the profile. If a GCDA file exists, add the profile to a list. Return the
profile list. */
struct gcov_info *
get_target_profiles_for_merge (struct gcov_info *src_profile)
{
struct gcov_info *gi_ptr;
read_profile_dir_init ();
for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
if (gcov_open (gi_ptr->filename, 1))
{
(void)read_gcda_file (gi_ptr->filename);
gcov_close ();
}
return gcov_info_head;
}
/* Deserialize gcov_info objects and associated filenames from the file
specified by FILENAME to create a source profile list. When FILENAME is
NULL, read from stdin. Use the filenames of the source profile list to get
a target profile list. Merge the source profile list into the target
profile list using weights W1 and W2. Return the list of merged gcov_info
objects. Return NULL if the list is empty. */
struct gcov_info *
gcov_profile_merge_stream (const char *filename, int w1, int w2)
{
struct gcov_info *tgt_profile;
struct gcov_info *src_profile;
if (!gcov_open (filename, 1))
{
fnotice (stderr, "%s:cannot open\n", filename);
return NULL;
}
src_profile = deserialize_profiles (filename ? filename : "<stdin>");
gcov_close ();
tgt_profile = get_target_profiles_for_merge (src_profile);
return gcov_profile_merge (tgt_profile, src_profile, w1, w2);
}
typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*);
/* Performing FN upon arc counters. */