[RFC/gcov,12/12] gcov-tool: Add merge-stream subcommand

Message ID 20220331113515.35764-13-sebastian.huber@embedded-brains.de
State New
Headers
Series Add merge-stream subcommand to gcov-tool |

Commit Message

Sebastian Huber March 31, 2022, 11:35 a.m. UTC
  gcc/

	* 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.
---
 gcc/gcov-tool.cc      | 76 ++++++++++++++++++++++++++++++++++++++++
 libgcc/libgcov-util.c | 80 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 156 insertions(+)
  

Comments

Martin Liška April 7, 2022, 8:36 a.m. UTC | #1
On 3/31/22 13:35, Sebastian Huber wrote:
> gcc/
> 
> 	* 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.
> ---
>   gcc/gcov-tool.cc      | 76 ++++++++++++++++++++++++++++++++++++++++
>   libgcc/libgcov-util.c | 80 +++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 156 insertions(+)
> 
> diff --git a/gcc/gcov-tool.cc b/gcc/gcov-tool.cc
> index d712715cf7e..d8572b184e9 100644
> --- a/gcc/gcov-tool.cc
> +++ b/gcc/gcov-tool.cc
> @@ -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] [stream-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"))
> diff --git a/libgcc/libgcov-util.c b/libgcc/libgcov-util.c
> index 622d5a9dc71..0fe60528b48 100644
> --- a/libgcc/libgcov-util.c
> +++ b/libgcc/libgcov-util.c
> @@ -735,6 +735,86 @@ gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile
>     return tgt_profile;
>   }

Please document the new added functions below.

>   
> +struct gcov_info *
> +consume_stream (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)

A space after ().

Martin

> +	    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;
> +}
> +
> +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;
> +}
> +
> +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 = consume_stream (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.  */
  

Patch

diff --git a/gcc/gcov-tool.cc b/gcc/gcov-tool.cc
index d712715cf7e..d8572b184e9 100644
--- a/gcc/gcov-tool.cc
+++ b/gcc/gcov-tool.cc
@@ -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] [stream-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"))
diff --git a/libgcc/libgcov-util.c b/libgcc/libgcov-util.c
index 622d5a9dc71..0fe60528b48 100644
--- a/libgcc/libgcov-util.c
+++ b/libgcc/libgcov-util.c
@@ -735,6 +735,86 @@  gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile
   return tgt_profile;
 }
 
+struct gcov_info *
+consume_stream (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;
+}
+
+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;
+}
+
+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 = consume_stream (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.  */