]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libgcc/libgcov-util.c
Use REST API for bug titles in mklog.
[thirdparty/gcc.git] / libgcc / libgcov-util.c
index 6dddec59490b4022291600c01e53589262d81d67..449638bc4b66fbe6e618991a86824b891ba2c2cc 100644 (file)
@@ -1,6 +1,6 @@
 /* Utility functions for reading gcda files into in-memory
    gcov_info structures and offline profile processing. */
-/* Copyright (C) 2014 Free Software Foundation, Inc.
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
    Contributed by Rong Xu <xur@google.com>.
 
 This file is part of GCC.
@@ -32,13 +32,13 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "diagnostic.h"
 #include "version.h"
 #include "demangle.h"
+#include "gcov-io.h"
 
 /* Borrowed from basic-block.h.  */
 #define RDIV(X,Y) (((X) + (Y) / 2) / (Y))
 
 extern gcov_position_t gcov_position();
 extern int gcov_is_error();
-extern gcov_unsigned_t gcov_max_filename;
 
 /* Verbose mode for debug.  */
 static int verbose;
@@ -53,7 +53,9 @@ void gcov_set_verbose (void)
 
 #include "obstack.h"
 #include <unistd.h>
+#ifdef HAVE_FTW_H
 #include <ftw.h>
+#endif
 
 static void tag_function (unsigned, unsigned);
 static void tag_blocks (unsigned, unsigned);
@@ -78,8 +80,8 @@ static int k_ctrs_mask[GCOV_COUNTERS];
 static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
 /* Number of kind of counters that have been seen.  */
 static int k_ctrs_types;
-/* The longest length of all the filenames.  */
-static int max_filename_len;
+/* The object summary being processed.  */
+static struct gcov_summary *curr_object_summary;
 
 /* Merge functions for counters.  */
 #define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE,
@@ -132,7 +134,6 @@ static const tag_format_t tag_table[] =
   {GCOV_TAG_ARCS, "ARCS", tag_arcs},
   {GCOV_TAG_LINES, "LINES", tag_lines},
   {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
-  {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
   {0, NULL, NULL}
 };
 
@@ -224,9 +225,8 @@ tag_counters (unsigned tag, unsigned length)
 static void
 tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
 {
-  struct gcov_summary summary;
-
-  gcov_read_summary (&summary);
+  curr_object_summary = (gcov_summary *) xcalloc (sizeof (gcov_summary), 1);
+  gcov_read_summary (curr_object_summary);
 }
 
 /* This function is called at the end of reading a gcda file.
@@ -240,7 +240,8 @@ read_gcda_finalize (struct gcov_info *obj_info)
   set_fn_ctrs (curr_fn_info);
   obstack_ptr_grow (&fn_info, curr_fn_info);
 
-  /* We set the following fields: merge, n_functions, and functions.  */
+  /* We set the following fields: merge, n_functions, functions
+     and summary.  */
   obj_info->n_functions = num_fn_info;
   obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info);
 
@@ -300,14 +301,13 @@ read_gcda_file (const char *filename)
   obstack_init (&fn_info);
   num_fn_info = 0;
   curr_fn_info = 0;
+  curr_object_summary = NULL;
   {
-    char *str_dup = (char*) xmalloc (strlen (filename) + 1);
-    int len;
+    size_t len = strlen (filename) + 1;
+    char *str_dup = (char*) xmalloc (len);
 
-    strcpy (str_dup, filename);
+    memcpy (str_dup, filename, len);
     obj_info->filename = str_dup;
-    if ((len = strlen (filename)) > max_filename_len)
-      max_filename_len = len;
   }
 
   /* Read stamp.  */
@@ -324,59 +324,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 %qx is invalid", 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 %qx is incorrectly nested",
+                         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",
+                     filename, actual_length - length);
+          else if (length > actual_length)
+           warning (0, "%s:record size mismatch %lu bytes unread",
+                     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" :
+                                 "%s:read error at %lu", filename,
+                   (long unsigned) gcov_position ());
+          break;
+        }
     }
 
   read_gcda_finalize (obj_info);
@@ -385,6 +385,7 @@ read_gcda_file (const char *filename)
   return obj_info;
 }
 
+#ifdef HAVE_FTW_H
 /* This will be called by ftw(). It opens and read a gcda file FILENAME.
    Return a non-zero value to stop the tree walk.  */
 
@@ -422,6 +423,7 @@ ftw_read_file (const char *filename,
 
   return 0;
 }
+#endif
 
 /* Initializer for reading a profile dir.  */
 
@@ -433,8 +435,7 @@ read_profile_dir_init (void)
 
 /* Driver for read a profile directory and convert into gcov_info list in memory.
    Return NULL on error,
-   Return the head of gcov_info list on success.
-   Note the file static variable GCOV_MAX_FILENAME is also set.  */
+   Return the head of gcov_info list on success.  */
 
 struct gcov_info *
 gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED)
@@ -457,16 +458,12 @@ gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNU
       fnotice (stderr, "%s is not a directory\n", dir_name);
       return NULL;
     }
+#ifdef HAVE_FTW_H
   ftw (".", ftw_read_file, 50);
-  ret = chdir (pwd);
+#endif
+  chdir (pwd);
   free (pwd);
 
-
-  /* gcov_max_filename is defined in libgcov.c that records the
-     max filename len. We need to set it here to allocate the
-     array for dumping.  */
-  gcov_max_filename = max_filename_len;
-
   return gcov_info_head;;
 }
 
@@ -588,7 +585,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;
@@ -677,10 +675,14 @@ gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile
     {
       gi_ptr = in_src_not_tgt[i];
       gcov_merge (gi_ptr, gi_ptr, w2 - 1);
+      gi_ptr->next = NULL;
       tgt_tail->next = gi_ptr;
       tgt_tail = gi_ptr;
     }
 
+  free (in_src_not_tgt);
+  free (tgt_infos);
+
   return 0;
 }
 
@@ -723,28 +725,11 @@ __gcov_time_profile_counter_op (gcov_type *counters ATTRIBUTE_UNUSED,
   /* Do nothing.  */
 }
 
-/* Performaing FN upon delta counters.  */
+/* Performing FN upon TOP N counters.  */
 
 static void
-__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters,
-                         counter_op_fn fn, void *data1, void *data2)
-{
-  unsigned i, n_measures;
-
-  gcc_assert (!(n_counters % 4));
-  n_measures = n_counters / 4;
-  for (i = 0; i < n_measures; i++, counters += 4)
-    {
-      counters[2] = fn (counters[2], data1, data2);
-      counters[3] = fn (counters[3], data1, data2);
-    }
-}
-
-/* Performing FN upon single counters.  */
-
-static void
-__gcov_single_counter_op (gcov_type *counters, unsigned n_counters,
-                          counter_op_fn fn, void *data1, void *data2)
+__gcov_topn_counter_op (gcov_type *counters, unsigned n_counters,
+                       counter_op_fn fn, void *data1, void *data2)
 {
   unsigned i, n_measures;
 
@@ -864,7 +849,476 @@ 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.
+    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++)
+      {
+        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;
+       unsigned c_num;
+       for (c_num = 0; c_num < ci_ptr->num; c_num++)
+         cum_1 += ci_ptr->values[c_num] / sum;
+      }
+    *cum_p = cum_1;
+    return 0.0;
+  }
+
+  for (f_ix = 0; f_ix < gcov_info1->n_functions; f_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;
+      unsigned c_num;
+      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;
+           }
+       }
+
+      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++)
+    {
+      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 (unsigned c_num = 0; c_num < ci_ptr->num; c_num++)
+       if (ci_ptr->values[c_num] > threshold)
+         return false;
+    }
+
+  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;
+
+/* 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;
+}
+
+/* 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)
+{
+  unsigned list1_cnt = 0, list2_cnt= 0, all_cnt;
+  unsigned int i, j;
+  const struct gcov_info *gi_ptr;
+  struct overlap_t *all_infos;
+
+  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;
+
+    }
+
+  free (all_infos);
+
+  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);
+
+  return prg_val;
+}
+
+/* Compute 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;
+}