Add overlap functionality to gcov-tool.
authorRong Xu <xur@google.com>
Wed, 8 Oct 2014 21:51:41 +0000 (21:51 +0000)
committerRong Xu <xur@gcc.gnu.org>
Wed, 8 Oct 2014 21:51:41 +0000 (21:51 +0000)
2014-10-08  Rong Xu  <xur@google.com>

* gcc/gcov-tool.c (profile_overlap): New driver function
        to compute profile overlap.
(print_overlap_usage_message): New.
(overlap_usage): New.
(do_overlap): New.
(print_usage): Add calls to overlap function.
(main): Ditto.
* libgcc/libgcov-util.c (read_gcda_file): Fix format.
(find_match_gcov_info): Ditto.
(calculate_2_entries): New.
(compute_one_gcov): Ditto.
(gcov_info_count_all_cold): Ditto.
(gcov_info_count_all_zero): Ditto.
(extract_file_basename): Ditto.
(get_file_basename): Ditto.
(set_flag): Ditto.
(matched_gcov_info): Ditto.
(calculate_overlap): Ditto.
(gcov_profile_overlap): Ditto.
* libgcc/libgcov-driver.c (compute_summary): Make
        it avavilable for external calls.
* gcc/doc/gcov-tool.texi: Add documentation.

From-SVN: r216015

gcc/ChangeLog
gcc/doc/gcov-tool.texi
gcc/gcov-tool.c
libgcc/ChangeLog
libgcc/libgcov-driver.c
libgcc/libgcov-util.c

index 289f22424f06e01ff208e3361f06701908a778a6..9491d8bbadbe522b3f692f1e87fa115964f34ead 100644 (file)
@@ -1,3 +1,14 @@
+2014-10-08  Rong Xu  <xur@google.com>
+
+       * gcov-tool.c (profile_overlap): New driver function
+       to compute profile overlap. 
+       (print_overlap_usage_message): New.
+       (overlap_usage): New.
+       (do_overlap): New.
+       (print_usage): Add calls to overlap function.
+       (main): Ditto.
+       * doc/gcov-tool.texi: Add documentation.
+
 2014-10-08  Steve Ellcey  <sellcey@mips.com>
 
        * config/mips/mti-linux.h (DRIVER_SELF_SPECS): Change
index ff8b9e22f4f4d19e4c2074f1091be676c367e612..3a6687289490aa09ad84bf1cae6629701f06049e 100644 (file)
@@ -103,8 +103,7 @@ in these kind of counters.
 @section Invoking @command{gcov-tool}
 
 @smallexample
-gcov-tool @r{[}@var{global-options}@r{]} SUB_COMMAND
-@r{[}@var{sub_command-options}@r{]} @var{profile_dir}
+gcov-tool @r{[}@var{global-options}@r{]} SUB_COMMAND @r{[}@var{sub_command-options}@r{]} @var{profile_dir}
 @end smallexample
 
 @command{gcov-tool} accepts the following options:
@@ -123,6 +122,15 @@ gcov-tool rewrite [rewrite-options] @var{directory}
      [@option{-o}|@option{--output} @var{directory}]
      [@option{-s}|@option{--scale} @var{float_or_simple-frac_value}]
      [@option{-n}|@option{--normalize} @var{long_long_value}]
+
+gcov-tool overlap [overlap-options] @var{directory1} @var{directory2}
+     [@option{-v}|@option{--verbose}]
+     [@option{-h}|@option{--hotonly}]
+     [@option{-f}|@option{--function}]
+     [@option{-F}|@option{--fullname}]
+     [@option{-o}|@option{--object}]
+     [@option{-t}|@option{--hot_threshold}] @var{float}
+
 @c man end
 @c man begin SEEALSO
 gpl(7), gfdl(7), fsf-funding(7), gcc(1), gcov(1) and the Info entry for
@@ -182,8 +190,42 @@ or simple fraction value form, such 1, 2, 2/3, and 5/3.
 @itemx --normalize <long_long_value>
 Normalize the profile. The specified value is the max counter value
 in the new profile.
+@end table
+
+@item overlap
+Computer the overlap score between the two specified profile directories.
+The overlap score is computed based on the arc profiles. It is defined as
+the sum of min (p1_counter[i] / p1_sum_all, p2_counter[i] / p2_sum_all),
+for all arc counter i, where p1_counter[i] and p2_counter[i] are two
+matched counters and p1_sum_all and p2_sum_all are the sum of counter
+values in profile 1 and profile 2, respectively.
+
+@table @gcctabopt
+@item -v
+@itemx --verbose
+Set the verbose mode.
+
+@item -h
+@itemx --hotonly
+Only print info for hot objects/functions.
 
+@item -f
+@itemx --function
+Print function level overlap score.
+
+@item -F
+@itemx --fullname
+Print full gcda filename.
+
+@item -o
+@itemx --object
+Print object level overlap score.
+
+@item -t @var{float}
+@itemx --hot_threshold <float>
+Set the threshold for hot counter value.
 @end table
+
 @end table
 
 @c man end
index 61e82a3dcb1d15e8061cfcabed3a40762e82a36e..db23bd7bc49322d73c15971f0bcfb6452e3e7c30 100644 (file)
@@ -39,6 +39,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include <getopt.h>
 
 extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, 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);
 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
@@ -368,6 +369,121 @@ do_rewrite (int argc, char **argv)
   return ret;
 }
 
+/* Driver function to computer the overlap score b/w profile D1 and D2.
+   Return 1 on error and 0 if OK.  */
+
+static int
+profile_overlap (const char *d1, const char *d2)
+{
+  struct gcov_info *d1_profile;
+  struct gcov_info *d2_profile;
+
+  d1_profile = gcov_read_profile_dir (d1, 0);
+  if (!d1_profile)
+    return 1;
+
+  if (d2)
+    {
+      d2_profile = gcov_read_profile_dir (d2, 0);
+      if (!d2_profile)
+        return 1;
+
+      return gcov_profile_overlap (d1_profile, d2_profile);
+    }
+
+  return 1;
+}
+
+/* Usage message for profile overlap.  */
+
+static void
+print_overlap_usage_message (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+
+  fnotice (file, "  overlap [options] <dir1> <dir2>       Compute the overlap of two profiles\n");
+  fnotice (file, "    -v, --verbose                       Verbose mode\n");
+  fnotice (file, "    -h, --hotonly                       Only print info for hot objects/functions\n");
+  fnotice (file, "    -f, --function                      Print function level info\n");
+  fnotice (file, "    -F, --fullname                      Print full filename\n");
+  fnotice (file, "    -o, --object                        Print object level info\n");
+  fnotice (file, "    -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
+
+}
+
+static const struct option overlap_options[] =
+{
+  { "verbose",                no_argument,       NULL, 'v' },
+  { "function",               no_argument,       NULL, 'f' },
+  { "fullname",               no_argument,       NULL, 'F' },
+  { "object",                 no_argument,       NULL, 'o' },
+  { "hotonly",                no_argument,       NULL, 'h' },
+  { "hot_threshold",          required_argument, NULL, 't' },
+  { 0, 0, 0, 0 }
+};
+
+/* Print overlap usage and exit.  */
+
+static void
+overlap_usage (void)
+{
+  fnotice (stderr, "Overlap subcomand usage:");
+  print_overlap_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+int overlap_func_level;
+int overlap_obj_level;
+int overlap_hot_only;
+int overlap_use_fullname;
+double overlap_hot_threshold = 0.005;
+
+/* Driver for profile overlap sub-command.  */
+
+static int
+do_overlap (int argc, char **argv)
+{
+  int opt;
+  int ret;
+
+  optind = 0;
+  while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
+    {
+      switch (opt)
+        {
+        case 'v':
+          verbose = true;
+          gcov_set_verbose ();
+          break;
+        case 'f':
+          overlap_func_level = 1;
+          break;
+        case 'F':
+          overlap_use_fullname = 1;
+          break;
+        case 'o':
+          overlap_obj_level = 1;
+          break;
+        case 'h':
+          overlap_hot_only = 1;
+          break;
+        case 't':
+          overlap_hot_threshold = atof (optarg);
+          break;
+        default:
+          overlap_usage ();
+        }
+    }
+
+  if (argc - optind == 2)
+    ret = profile_overlap (argv[optind], argv[optind+1]);
+  else
+    overlap_usage ();
+
+  return ret;
+}
+
+
 /* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
    otherwise the output of --help.  */
 
@@ -383,6 +499,7 @@ print_usage (int error_p)
   fnotice (file, "  -v, --version                         Print version number, then exit\n");
   print_merge_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",
            bug_report_url);
   exit (status);
@@ -471,6 +588,8 @@ main (int argc, char **argv)
     return do_merge (argc - optind, argv + optind);
   else if (!strcmp (sub_command, "rewrite"))
     return do_rewrite (argc - optind, argv + optind);
+  else if (!strcmp (sub_command, "overlap"))
+    return do_overlap (argc - optind, argv + optind);
 
   print_usage (true);
 }
index 248c1786ff5cecad34460dc97d0d4e85bd1601a7..1d53defc95ec149df92eb7b91d71b2a75dd77a08 100644 (file)
@@ -1,3 +1,20 @@
+2014-10-08  Rong Xu  <xur@google.com>
+
+       * libgcov-util.c (read_gcda_file): Fix format.
+       (find_match_gcov_info): Ditto.
+       (calculate_2_entries): New.
+       (compute_one_gcov): Ditto.
+       (gcov_info_count_all_cold): Ditto.
+       (gcov_info_count_all_zero): Ditto.
+       (extract_file_basename): Ditto.
+       (get_file_basename): Ditto.
+       (set_flag): Ditto.
+       (matched_gcov_info): Ditto.
+       (calculate_overlap): Ditto.
+       (gcov_profile_overlap): Ditto.
+       * libgcov-driver.c (compute_summary): Make
+       it avavilable for external calls.
+
 2014-10-06  Rong Xu  <xur@google.com>
 
        * Makefile.in: Ditto.
index 754584de7ce0af2567ff662f2c8001d73520a067..2ff878f836c8323f10645e96602f98aa766ffed3 100644 (file)
@@ -274,7 +274,10 @@ static struct gcov_summary_buffer *sum_buffer;
    It computes and returns CRC32 and stored summary in THIS_PRG.
    Also determines the longest filename length of the info files.  */
 
-static gcov_unsigned_t
+#if !IN_GCOV_TOOL
+static
+#endif
+gcov_unsigned_t
 compute_summary (struct gcov_info *list, struct gcov_summary *this_prg,
                 size_t *max_length)
 {
index 38d434ae954b51686d2d58432694680a58ba2cde..10771332b917e33f8c9e146b51ead76caf65ba23 100644 (file)
@@ -319,59 +319,59 @@ read_gcda_file (const char *filename)
 
       tag = gcov_read_unsigned ();
       if (!tag)
-       break;
+        break;
       length = gcov_read_unsigned ();
       base = gcov_position ();
       mask = GCOV_TAG_MASK (tag) >> 1;
       for (tag_depth = 4; mask; mask >>= 8)
-       {
-         if (((mask & 0xff) != 0xff))
-           {
-             warning (0, "%s:tag `%x' is invalid\n", filename, tag);
-             break;
-           }
-         tag_depth--;
-       }
+        {
+          if (((mask & 0xff) != 0xff))
+            {
+              warning (0, "%s:tag `%x' is invalid\n", filename, tag);
+              break;
+            }
+          tag_depth--;
+        }
       for (format = tag_table; format->name; format++)
-       if (format->tag == tag)
-         goto found;
+        if (format->tag == tag)
+          goto found;
       format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
     found:;
       if (tag)
-       {
-         if (depth && depth < tag_depth)
-           {
-             if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
-               warning (0, "%s:tag `%x' is incorrectly nested\n",
-                       filename, tag);
-           }
-         depth = tag_depth;
-         tags[depth - 1] = tag;
-       }
+        {
+          if (depth && depth < tag_depth)
+            {
+              if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
+                warning (0, "%s:tag `%x' is incorrectly nested\n",
+                         filename, tag);
+            }
+          depth = tag_depth;
+          tags[depth - 1] = tag;
+        }
 
       if (format->proc)
         {
-         unsigned long actual_length;
+          unsigned long actual_length;
 
-         (*format->proc) (tag, length);
+          (*format->proc) (tag, length);
 
-         actual_length = gcov_position () - base;
-         if (actual_length > length)
-           warning (0, "%s:record size mismatch %lu bytes overread\n",
-                   filename, actual_length - length);
-         else if (length > actual_length)
-           warning (0, "%s:record size mismatch %lu bytes unread\n",
-                   filename, length - actual_length);
-       }
+          actual_length = gcov_position () - base;
+          if (actual_length > length)
+            warning (0, "%s:record size mismatch %lu bytes overread\n",
+                     filename, actual_length - length);
+          else if (length > actual_length)
+            warning (0, "%s:record size mismatch %lu bytes unread\n",
+                     filename, length - actual_length);
+       }
 
       gcov_sync (base, length);
       if ((error = gcov_is_error ()))
-       {
-         warning (0, error < 0 ? "%s:counter overflow at %lu\n" :
-                                 "%s:read error at %lu\n", filename,
-                 (long unsigned) gcov_position ());
-         break;
-       }
+        {
+          warning (0, error < 0 ? "%s:counter overflow at %lu\n" :
+                                  "%s:read error at %lu\n", filename,
+                   (long unsigned) gcov_position ());
+          break;
+        }
     }
 
   read_gcda_finalize (obj_info);
@@ -577,7 +577,8 @@ gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
    Return NULL if there is no match.  */
 
 static struct gcov_info *
-find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info)
+find_match_gcov_info (struct gcov_info **array, int size,
+                     struct gcov_info *info)
 {
   struct gcov_info *gi_ptr;
   struct gcov_info *ret = NULL;
@@ -872,7 +873,530 @@ gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val)
 
   scale_factor = (float)max_val / curr_max_val;
   if (verbose)
-    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
+    fnotice (stdout, "max_val is %"PRId64"\n", curr_max_val);
 
   return gcov_profile_scale (profile, scale_factor, 0, 0);
 }
+
+/* The following variables are defined in gcc/gcov-tool.c.  */
+extern int overlap_func_level;
+extern int overlap_obj_level;
+extern int overlap_hot_only;
+extern int overlap_use_fullname;
+extern double overlap_hot_threshold;
+
+/* Compute the overlap score of two values. The score is defined as:
+    min (V1/SUM_1, V2/SUM_2)  */
+
+static double
+calculate_2_entries (const unsigned long v1, const unsigned long v2,
+                     const double sum_1, const double sum_2)
+{
+  double val1 = (sum_1 == 0.0 ? 0.0 : v1/sum_1);
+  double val2 = (sum_2 == 0.0 ? 0.0 : v2/sum_2);
+
+  if (val2 < val1)
+    val1 = val2;
+
+  return val1;
+}
+
+/*  Compute the overlap score between GCOV_INFO1 and GCOV_INFO2.
+    SUM_1 is the sum_all for profile1 where GCOV_INFO1 belongs.
+    SUM_2 is the sum_all for profile2 where GCOV_INFO2 belongs.
+    This function also updates cumulative score CUM_1_RESULT and
+    CUM_2_RESULT.  */
+
+static double
+compute_one_gcov (const struct gcov_info *gcov_info1,
+                  const struct gcov_info *gcov_info2,
+                  const double sum_1, const double sum_2,
+                  double *cum_1_result, double *cum_2_result)
+{
+  unsigned f_ix;
+  double ret = 0;
+  double cum_1 = 0, cum_2 = 0;
+  const struct gcov_info *gcov_info = 0;
+  double *cum_p;
+  double sum;
+
+  gcc_assert (gcov_info1 || gcov_info2);
+  if (!gcov_info1)
+    {
+      gcov_info = gcov_info2;
+      cum_p = cum_2_result;
+      sum = sum_2;
+      *cum_1_result = 0;
+    } else
+  if (!gcov_info2)
+    {
+      gcov_info = gcov_info1;
+      cum_p = cum_1_result;
+      sum = sum_1;
+      *cum_2_result = 0;
+    }
+
+  if (gcov_info)
+  {
+    for (f_ix = 0; f_ix < gcov_info->n_functions; f_ix++)
+      {
+        unsigned t_ix;
+        const struct gcov_fn_info *gfi_ptr = gcov_info->functions[f_ix];
+        if (!gfi_ptr || gfi_ptr->key != gcov_info)
+          continue;
+        const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
+        for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+          {
+            unsigned c_num;
+
+            if (!gcov_info->merge[t_ix])
+              continue;
+
+            for (c_num = 0; c_num < ci_ptr->num; c_num++)
+              {
+                cum_1 += ci_ptr->values[c_num] / sum;
+              }
+            ci_ptr++;
+          }
+      }
+    *cum_p = cum_1;
+    return 0.0;
+  }
+
+  for (f_ix = 0; f_ix < gcov_info1->n_functions; f_ix++)
+    {
+      unsigned t_ix;
+      double func_cum_1 = 0.0;
+      double func_cum_2 = 0.0;
+      double func_val = 0.0;
+      int nonzero = 0;
+      int hot = 0;
+      const struct gcov_fn_info *gfi_ptr1 = gcov_info1->functions[f_ix];
+      const struct gcov_fn_info *gfi_ptr2 = gcov_info2->functions[f_ix];
+
+      if (!gfi_ptr1 || gfi_ptr1->key != gcov_info1)
+        continue;
+      if (!gfi_ptr2 || gfi_ptr2->key != gcov_info2)
+        continue;
+
+      const struct gcov_ctr_info *ci_ptr1 = gfi_ptr1->ctrs;
+      const struct gcov_ctr_info *ci_ptr2 = gfi_ptr2->ctrs;
+      for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+        {
+          unsigned c_num;
+
+          if (!gcov_info1->merge[t_ix])
+            continue;
+
+          for (c_num = 0; c_num < ci_ptr1->num; c_num++)
+            {
+              if (ci_ptr1->values[c_num] | ci_ptr2->values[c_num])
+                {
+                  func_val += calculate_2_entries (ci_ptr1->values[c_num],
+                                          ci_ptr2->values[c_num],
+                                          sum_1, sum_2);
+
+                  func_cum_1 += ci_ptr1->values[c_num] / sum_1;
+                  func_cum_2 += ci_ptr2->values[c_num] / sum_2;
+                  nonzero = 1;
+                  if (ci_ptr1->values[c_num] / sum_1 >= overlap_hot_threshold ||
+                      ci_ptr2->values[c_num] / sum_2 >= overlap_hot_threshold)
+                    hot = 1;
+                }
+            }
+          ci_ptr1++;
+          ci_ptr2++;
+        }
+      ret += func_val;
+      cum_1 += func_cum_1;
+      cum_2 += func_cum_2;
+      if (overlap_func_level && nonzero && (!overlap_hot_only || hot))
+        {
+          printf("   \tfunc_id=%10d \toverlap =%6.5f%% (%5.5f%% %5.5f%%)\n",
+                 gfi_ptr1->ident, func_val*100, func_cum_1*100, func_cum_2*100);
+        }
+    }
+  *cum_1_result = cum_1;
+  *cum_2_result = cum_2;
+  return ret;
+}
+
+/* Test if all counter values in this GCOV_INFO are cold.
+   "Cold" is defined as the counter value being less than
+   or equal to THRESHOLD.  */
+
+static bool
+gcov_info_count_all_cold (const struct gcov_info *gcov_info,
+                          gcov_type threshold)
+{
+  unsigned f_ix;
+
+  for (f_ix = 0; f_ix < gcov_info->n_functions; f_ix++)
+    {
+      unsigned t_ix;
+      const struct gcov_fn_info *gfi_ptr = gcov_info->functions[f_ix];
+
+      if (!gfi_ptr || gfi_ptr->key != gcov_info)
+        continue;
+      const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
+      for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+        {
+          unsigned c_num;
+
+          if (!gcov_info->merge[t_ix])
+            continue;
+
+          for (c_num = 0; c_num < ci_ptr->num; c_num++)
+            {
+              if (ci_ptr->values[c_num] > threshold)
+                return false;
+            }
+          ci_ptr++;
+        }
+    }
+
+  return true;
+}
+
+/* Test if all counter values in this GCOV_INFO are 0.  */
+
+static bool
+gcov_info_count_all_zero (const struct gcov_info *gcov_info)
+{
+  return gcov_info_count_all_cold (gcov_info, 0);
+}
+
+/* A pair of matched GCOV_INFO.
+   The flag is a bitvector:
+     b0: obj1's all counts are 0;
+     b1: obj1's all counts are cold (but no 0);
+     b2: obj1 is hot;
+     b3: no obj1 to match obj2;
+     b4: obj2's all counts are 0;
+     b5: obj2's all counts are cold (but no 0);
+     b6: obj2 is hot;
+     b7: no obj2 to match obj1;
+ */
+struct overlap_t {
+   const struct gcov_info *obj1;
+   const struct gcov_info *obj2;
+   char flag;
+};
+
+#define FLAG_BOTH_ZERO(flag) ((flag & 0x1) && (flag & 0x10))
+#define FLAG_BOTH_COLD(flag) ((flag & 0x2) && (flag & 0x20))
+#define FLAG_ONE_HOT(flag) ((flag & 0x4) || (flag & 0x40))
+
+/* Cumlative overlap dscore for profile1 and profile2.  */
+static double overlap_sum_1, overlap_sum_2;
+
+/* sum_all for profile1 and profile2.  */
+static gcov_type p1_sum_all, p2_sum_all;
+
+/* run_max for profile1 and profile2.  */
+static gcov_type p1_run_max, p2_run_max;
+
+/* The number of gcda files in the profiles.  */
+static unsigned gcda_files[2];
+
+/* The number of unique gcda files in the profiles
+   (not existing in the other profile).  */
+static unsigned unique_gcda_files[2];
+
+/* The number of gcda files that all counter values are 0.  */
+static unsigned zero_gcda_files[2];
+
+/* The number of gcda files that all counter values are cold (but not 0).  */
+static unsigned cold_gcda_files[2];
+
+/* The number of gcda files that includes hot counter values.  */
+static unsigned hot_gcda_files[2];
+
+/* The number of gcda files with hot count value in either profiles.  */
+static unsigned both_hot_cnt;
+
+/* The number of gcda files with all counts cold (but not 0) in
+   both profiles. */
+static unsigned both_cold_cnt;
+
+/* The number of gcda files with all counts 0 in both profiles.  */
+static unsigned both_zero_cnt;
+
+/* Extract the basename of the filename NAME.  */
+
+static char *
+extract_file_basename (const char *name)
+{
+  char *str;
+  int len = 0;
+  char *path = xstrdup (name);
+  char sep_str[2];
+
+  sep_str[0] = DIR_SEPARATOR;
+  sep_str[1] = 0;
+  str = strstr(path, sep_str);
+  do{
+      len = strlen(str) + 1;
+      path = &path[strlen(path) - len + 2];
+      str = strstr(path, sep_str);
+  } while(str);
+
+  return path;
+}
+
+/* Utility function to get the filename.  */
+
+static const char *
+get_file_basename (const char *name)
+{
+  if (overlap_use_fullname)
+    return name;
+  return extract_file_basename (name);
+}
+
+/* A utility function to set the flag for the gcda files.  */
+
+static void
+set_flag (struct overlap_t *e)
+{
+  char flag = 0;
+
+  if (!e->obj1)
+    {
+      unique_gcda_files[1]++;
+      flag = 0x8;
+    }
+  else
+    {
+      gcda_files[0]++;
+      if (gcov_info_count_all_zero (e->obj1))
+        {
+          zero_gcda_files[0]++;
+          flag = 0x1;
+        }
+      else
+      if (gcov_info_count_all_cold (e->obj1, overlap_sum_1
+                             * overlap_hot_threshold))
+        {
+          cold_gcda_files[0]++;
+          flag = 0x2;
+        }
+      else
+        {
+          hot_gcda_files[0]++;
+          flag = 0x4;
+        }
+    }
+
+  if (!e->obj2)
+    {
+      unique_gcda_files[0]++;
+      flag |= (0x8 << 4);
+    }
+  else
+    {
+      gcda_files[1]++;
+      if (gcov_info_count_all_zero (e->obj2))
+        {
+          zero_gcda_files[1]++;
+          flag |= (0x1 << 4);
+        }
+      else
+      if (gcov_info_count_all_cold (e->obj2, overlap_sum_2
+                             * overlap_hot_threshold))
+        {
+          cold_gcda_files[1]++;
+          flag |= (0x2 << 4);
+        }
+      else
+        {
+          hot_gcda_files[1]++;
+          flag |= (0x4 << 4);
+        }
+    }
+
+  gcc_assert (flag);
+  e->flag = flag;
+}
+
+/* Test if INFO1 and INFO2 are from the matched source file.
+   Return 1 if they match; return 0 otherwise.  */
+
+static int
+matched_gcov_info (const struct gcov_info *info1, const struct gcov_info *info2)
+{
+  /* For FDO, we have to match the name. This can be expensive.
+     Maybe we should use hash here.  */
+  if (strcmp (info1->filename, info2->filename))
+    return 0;
+
+  if (info1->n_functions != info2->n_functions)
+    {
+      fnotice (stderr, "mismatched profiles in %s (%d functions"
+                       " vs %d functions)\n",
+                       info1->filename,
+                       info1->n_functions,
+                       info2->n_functions);
+      return 0;
+    }
+  return 1;
+}
+
+/* Defined in libgcov-driver.c.  */
+extern gcov_unsigned_t compute_summary (struct gcov_info *,
+                 struct gcov_summary *, size_t *);
+
+/* Compute the overlap score of two profiles with the head of GCOV_LIST1 and
+   GCOV_LIST1. Return a number ranging from [0.0, 1.0], with 0.0 meaning no
+   match and 1.0 meaning a perfect match.  */
+
+static double
+calculate_overlap (struct gcov_info *gcov_list1,
+                   struct gcov_info *gcov_list2)
+{
+  struct gcov_summary this_prg;
+  unsigned list1_cnt = 0, list2_cnt= 0, all_cnt;
+  unsigned int i, j;
+  size_t max_length;
+  const struct gcov_info *gi_ptr;
+  struct overlap_t *all_infos;
+
+  compute_summary (gcov_list1, &this_prg, &max_length);
+  overlap_sum_1 = (double) (this_prg.ctrs[0].sum_all);
+  p1_sum_all = this_prg.ctrs[0].sum_all;
+  p1_run_max = this_prg.ctrs[0].run_max;
+  compute_summary (gcov_list2, &this_prg, &max_length);
+  overlap_sum_2 = (double) (this_prg.ctrs[0].sum_all);
+  p2_sum_all = this_prg.ctrs[0].sum_all;
+  p2_run_max = this_prg.ctrs[0].run_max;
+
+  for (gi_ptr = gcov_list1; gi_ptr; gi_ptr = gi_ptr->next)
+    list1_cnt++;
+  for (gi_ptr = gcov_list2; gi_ptr; gi_ptr = gi_ptr->next)
+    list2_cnt++;
+  all_cnt = list1_cnt + list2_cnt;
+  all_infos = (struct overlap_t *) xmalloc (sizeof (struct overlap_t)
+               * all_cnt * 2);
+  gcc_assert (all_infos);
+
+  i = 0;
+  for (gi_ptr = gcov_list1; gi_ptr; gi_ptr = gi_ptr->next, i++)
+    {
+      all_infos[i].obj1 = gi_ptr;
+      all_infos[i].obj2 = 0;
+    }
+
+  for (gi_ptr = gcov_list2; gi_ptr; gi_ptr = gi_ptr->next, i++)
+    {
+      all_infos[i].obj1 = 0;
+      all_infos[i].obj2 = gi_ptr;
+    }
+
+  for (i = list1_cnt; i < all_cnt; i++)
+    {
+      if (all_infos[i].obj2 == 0)
+        continue;
+      for (j = 0; j < list1_cnt; j++)
+        {
+          if (all_infos[j].obj2 != 0)
+            continue;
+          if (matched_gcov_info (all_infos[i].obj2, all_infos[j].obj1))
+            {
+              all_infos[j].obj2 = all_infos[i].obj2;
+              all_infos[i].obj2 = 0;
+              break;
+            }
+        }
+    }
+
+  for (i = 0; i < all_cnt; i++)
+    if (all_infos[i].obj1 || all_infos[i].obj2)
+      {
+        set_flag (all_infos + i);
+        if (FLAG_ONE_HOT (all_infos[i].flag))
+            both_hot_cnt++;
+        if (FLAG_BOTH_COLD(all_infos[i].flag))
+            both_cold_cnt++;
+        if (FLAG_BOTH_ZERO(all_infos[i].flag))
+            both_zero_cnt++;
+      }
+
+  double prg_val = 0;
+  double sum_val = 0;
+  double sum_cum_1 = 0;
+  double sum_cum_2 = 0;
+
+  for (i = 0; i < all_cnt; i++)
+    {
+      double val;
+      double cum_1, cum_2;
+      const char *filename;
+
+      if (all_infos[i].obj1 == 0 && all_infos[i].obj2 == 0)
+        continue;
+      if (FLAG_BOTH_ZERO (all_infos[i].flag))
+          continue;
+
+      if (all_infos[i].obj1)
+        filename = get_file_basename (all_infos[i].obj1->filename);
+      else
+        filename = get_file_basename (all_infos[i].obj2->filename);
+
+      if (overlap_func_level)
+        printf("\n   processing %36s:\n", filename);
+
+      val = compute_one_gcov (all_infos[i].obj1, all_infos[i].obj2,
+          overlap_sum_1, overlap_sum_2, &cum_1, &cum_2);
+
+      if (overlap_obj_level && (!overlap_hot_only || FLAG_ONE_HOT (all_infos[i].flag)))
+        {
+          printf("   obj=%36s  overlap = %6.2f%% (%5.2f%% %5.2f%%)\n",
+                  filename, val*100, cum_1*100, cum_2*100);
+          sum_val += val;
+          sum_cum_1 += cum_1;
+          sum_cum_2 += cum_2;
+        }
+
+      prg_val += val;
+
+    }
+
+  if (overlap_obj_level)
+    printf("   SUM:%36s  overlap = %6.2f%% (%5.2f%% %5.2f%%)\n",
+           "", sum_val*100, sum_cum_1*100, sum_cum_2*100);
+
+  printf ("  Statistics:\n"
+          "                    profile1_#     profile2_#       overlap_#\n");
+  printf ("    gcda files:  %12u\t%12u\t%12u\n", gcda_files[0], gcda_files[1],
+                                          gcda_files[0]-unique_gcda_files[0]);
+  printf ("  unique files:  %12u\t%12u\n", unique_gcda_files[0],
+                                        unique_gcda_files[1]);
+  printf ("     hot files:  %12u\t%12u\t%12u\n", hot_gcda_files[0],
+                                            hot_gcda_files[1], both_hot_cnt);
+  printf ("    cold files:  %12u\t%12u\t%12u\n", cold_gcda_files[0],
+                                            cold_gcda_files[1], both_cold_cnt);
+  printf ("    zero files:  %12u\t%12u\t%12u\n", zero_gcda_files[0],
+                                            zero_gcda_files[1], both_zero_cnt);
+  printf ("       sum_all:  %12"PRId64"\t%12"PRId64"\n", p1_sum_all, p2_sum_all);
+  printf ("       run_max:  %12"PRId64"\t%12"PRId64"\n", p1_run_max, p2_run_max);
+
+  return prg_val;
+}
+
+/* Computer the overlap score of two lists of gcov_info objects PROFILE1 and PROFILE2.
+   Return 0 on success: without mismatch. Reutrn 1 on error.  */
+
+int
+gcov_profile_overlap (struct gcov_info *profile1, struct gcov_info *profile2)
+{
+  double result;
+
+  result = calculate_overlap (profile1, profile2);
+
+  if (result > 0)
+    {
+      printf("\nProgram level overlap result is %3.2f%%\n\n", result*100);
+      return 0;
+    }
+  return 1;
+}