diff mbox

[google,gcc-4_8] gcov-tool: some new LIPO supports.

Message ID CAF1bQ=RsaZiTes0Xyt35ivoNkcgQPLS0rRcaow7i6LLeB+ys6w@mail.gmail.com
State New
Headers show

Commit Message

Rong Xu Jan. 30, 2014, 9:20 p.m. UTC
Comments are inlined. New patch attached to this email.

-Rong

On Thu, Jan 30, 2014 at 12:39 PM, Xinliang David Li <davidxl@google.com> wrote:
> On Wed, Jan 29, 2014 at 4:24 PM, Rong Xu <xur@google.com> wrote:
>> Hi,
>>
>> The attached patch adds some new features to gcov-tool. It aims to
>> rewrite LIPO profile for reuse, debug or performance tuning purpose.
>>
>> -l, --modu_list <file>              Only use the modules in this file
>
> I think in verbose mode, the tool should emit warnings when a module
> is trimmed due to this option. This can be used in regression test.
>

In previous patch, this warning is emitted unconventionally (not
guarded by verbose flag).
I changed it to under verbose mode in this patch.

>> -r, --string                             Replace string in path
>
> --path_substr_replace or something in that line.
>

Done.

>> -u, --use_imports_file             Use the grouping in import files.
>>
>
> Is there a path in code to auto test this?

As we discussed offline. This can be verified by a separated script.

>
>> -l uses only the modules in specified file to compute module grouping
>> (and subsequent dumping).
>> -r replaces the pattern specified in the argument. The format is:
>> old_str1:new_str1[,old_str2:new_str2]*, only the first occurrence is
>> replaced.
>> -u skips the run-time module grouping computation and reuses the one
>> comes with the profiles (which is user editable).
>>
>> Tested with profiles from google internal benchmarks.
>>
>
> Also use strcasestr for case insenstive operation.

Done.

>
> David
>
>> -Rong
diff mbox

Patch

Index: gcc/gcov-tool.c
===================================================================
--- gcc/gcov-tool.c	(revision 207316)
+++ gcc/gcov-tool.c	(working copy)
@@ -29,6 +29,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
 #include "coretypes.h"
 #include "tm.h"
 #include "intl.h"
+#include "hashtab.h"
 #include "diagnostic.h"
 #include "version.h"
 #include "gcov-io.h"
@@ -38,6 +39,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
 #include <ftw.h>
 #include <getopt.h>
 #include "params.h"
+#include <string.h>
 
 extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
@@ -47,9 +49,11 @@  extern struct gcov_info* gcov_read_profile_dir (co
 extern void gcov_exit (void);
 extern void set_gcov_list (struct gcov_info *);
 extern void gcov_set_verbose (void);
+extern void set_use_existing_grouping (void);
+extern void set_use_modu_list (void);
+extern void lipo_set_substitute_string (const char *);
 
-static int verbose;
-
+/* The following defines are needed by dyn-ipa.c.  */
 gcov_unsigned_t __gcov_lipo_grouping_algorithm;
 gcov_unsigned_t __gcov_lipo_merge_modu_edges;
 gcov_unsigned_t __gcov_lipo_weak_inclusion;
@@ -60,6 +64,8 @@  gcov_unsigned_t __gcov_lipo_random_seed;
 gcov_unsigned_t __gcov_lipo_dump_cgraph;
 gcov_unsigned_t __gcov_lipo_propagate_scale;
 
+static int verbose;
+
 /* Remove file NAME if it has a gcda suffix. */
 
 static int
@@ -285,6 +291,187 @@  profile_rewrite (const char *d1, const char *out,
   return 0;
 }
 
+/* This is the hashtab entry to store a name and mod_id pair. */
+typedef struct {
+  const char *name;
+  unsigned id;
+} mod_name_id;
+
+/* Hash and comparison functions for strings.  */
+
+static unsigned
+mod_name_id_htab_hash (const void *s_p)
+{
+  const char *s = ((const mod_name_id *) s_p)->name;
+  return (*htab_hash_string) (s);
+}
+
+static int
+mod_name_id_hash_eq (const void *s1_p, const void *s2_p)
+{
+  return strcmp (((const mod_name_id *) s1_p)->name,
+                 ((const mod_name_id *) s2_p)->name) == 0;
+}
+
+static htab_t mod_name_id_hash_table;
+
+/* Look up an entry in the hash table. STRING is the module name.
+   CREATE controls to insert to htab or not.
+   If (*ID_P != 0), we write (*ID_P) to htab.
+   If (*ID_P == 0), we write module_id to (*ID_P).
+   return 1 if an entry is found and otherwise 0.  */
+
+static int
+module_name_hash_lookup (const char *string, unsigned *id_p, int create)
+{
+  void **e;
+  mod_name_id t;
+
+  t.name = string;
+  e = htab_find_slot (mod_name_id_hash_table, &t,
+                      create ? INSERT : NO_INSERT);
+  if (e == NULL)
+    return 0;
+  if (*e == NULL)
+    {
+      *e = XNEW (mod_name_id *);
+      (*(mod_name_id **)e)->name = xstrdup (string);
+    }
+      if (id_p)
+        {
+          if (*id_p != 0)
+            (*(mod_name_id **)e)->id = *id_p;
+          else
+            *id_p = (*(mod_name_id **)e)->id;
+        }
+  return 1;
+}
+
+/* Return 1 if NAME is of a source type that LIPO targets.
+   Return 0 otherwise.  */
+
+static int
+is_lipo_source_type (char *name)
+{
+  char *p;
+
+  if (strcasestr (name, ".c") ||
+      strcasestr (name, ".cc") ||
+      strcasestr (name, ".cpp") ||
+      strcasestr (name, ".c++"))
+    return 1;
+
+  if ((p = strcasestr (name, ".proto")) != NULL)
+    {
+      strcpy (p, ".pb.cc");
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Convert/process the names from dependence query to a
+   stardard format. Return NULL if this is not a lipo
+   target source. */
+
+static char *
+lipo_process_name_string (char *name)
+{
+  char *p;
+
+  if (name == NULL)
+    return NULL;
+  if (strlen (name) == 0)
+    return NULL;
+
+  if (!is_lipo_source_type (name))
+    return NULL;
+
+  /* Overwrite ':' with '/'.  */
+  if ((p = strchr (name, ':')) != NULL)
+    *p = '/';
+
+  /* Remove "//".  */
+  if (name[0] == '/' && name[1] =='/')
+    name += 2;
+
+  return name;
+}
+
+/* Store the list of source modules in INPUT_FILE to internal hashtab.  */
+
+static int
+lipo_process_modu_list (const char *input_file)
+{
+  FILE *fd;
+  char *line = NULL;
+  size_t linecap = 0;
+  char *name;
+
+  set_use_modu_list ();
+
+  if ((fd = fopen (input_file, "r")) == NULL)
+    {
+      fnotice (stderr, "Cannot open %s\n", input_file);
+      return -1;
+    }
+
+  /* Read all the modules */
+  while (getline (&line, &linecap, fd) != -1)
+    {
+      name = strtok (line, " \t\n");
+      name = lipo_process_name_string (name);
+      if (name)
+        module_name_hash_lookup (name, 0, INSERT);
+
+      free (line);
+      line = NULL;
+    }
+
+  return 0;
+}
+
+#define GENFILE_PREFIX "/genfiles/"
+
+/* Return 1 if module NAME is availale to be used in the target
+   profile.  CREATE controls to insert to htab or not.
+   If (*ID_P != 0), we write (*ID_P) to htab.
+   If (*ID_P == 0), we write module_id to (*ID_P).
+   return 1 if an entry is found and otherwise 0.  */
+
+int
+is_module_available (const char *name, unsigned *id_p, int create)
+{
+  char *buf, *p;
+  int ret;
+
+  if (mod_name_id_hash_table == NULL)
+    return 1;
+
+  buf = xstrdup (name);
+  /* Remove genfile string.  */
+  if ((p = strstr (buf, GENFILE_PREFIX)) != NULL)
+    p += strlen (GENFILE_PREFIX);
+  else
+    p = buf;
+
+   ret = module_name_hash_lookup (p, id_p, create);
+   free (buf);
+   return ret;
+}
+
+/* Return module_ident for module NAME.
+   return 0 if the module NAME is not available.  */
+
+int
+get_module_id_from_name (const char *name)
+{
+  unsigned mod_id = 0;
+  if (is_module_available (name, &mod_id, 0) == 1)
+    return mod_id;
+  return 0;
+}
+
 /* Usage function for profile rewrite.  */
 
 static void
@@ -295,7 +482,10 @@  print_rewrite_usage_message (int error_p)
   fnotice (file, "  rewrite [options] <dir>               Rewrite coverage file contents\n");
   fnotice (file, "    -v, --verbose                       Verbose mode\n");
   fnotice (file, "    -o, --output <dir>                  Output directory\n");
+  fnotice (file, "    -l, --modu_list <file>              Only use the modules in this file\n");
+  fnotice (file, "    -r, --path_substr_replace <str>     Replace string in path\n");
   fnotice (file, "    -s, --scale <float or simple-frac>  Scale the profile counters\n");
+  fnotice (file, "    -u, --use_imports_file <file>       Use the grouping in import files.\n");
   fnotice (file, "    -n, --normalize <long long>         Normalize the profile\n");
 }
 
@@ -303,7 +493,10 @@  static const struct option rewrite_options[] =
 {
   { "verbose",                no_argument,       NULL, 'v' },
   { "output",                 required_argument, NULL, 'o' },
+  { "modu_list",              required_argument, NULL, 'l' },
+  { "string",                 required_argument, NULL, 'r' },
   { "scale",                  required_argument, NULL, 's' },
+  { "use_imports_file",       no_argument,       NULL, 'u' },
   { "normalize",              required_argument, NULL, 'n' },
   { 0, 0, 0, 0 }
 };
@@ -331,8 +524,11 @@  do_rewrite (int argc, char **argv)
   int numerator = -1;
   int denominator = -1;
 
+  mod_name_id_hash_table = htab_create (500, mod_name_id_htab_hash,
+                                        mod_name_id_hash_eq, NULL);
+
   optind = 0;
-  while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
+  while ((opt = getopt_long (argc, argv, "vo:l:r:s:un:", rewrite_options, NULL)) != -1)
     {
       switch (opt)
         {
@@ -343,6 +539,15 @@  do_rewrite (int argc, char **argv)
         case 'o':
           output_dir = optarg;
           break;
+        case 'l':
+          lipo_process_modu_list (optarg);
+          break;
+        case 'r':
+          lipo_set_substitute_string (optarg);
+          break;
+        case 'u':
+          set_use_existing_grouping ();
+          break;
         case 'n':
           if (scale != 1.0)
             {
@@ -437,7 +642,7 @@  static void
 print_version (void)
 {
   fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
-  fprintf (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
+  fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
            _("(C)"));
   fnotice (stdout,
            _("This is free software; see the source for copying conditions.\n"
@@ -496,7 +701,7 @@  process_args (int argc, char **argv)
           sscanf (optarg, "%d", &ret);
           if (ret != 0 && ret != 1)
             {
-              fprintf (stderr, "LIPO grouping algorithm can only be 0 or 1. \n");
+              fnotice (stderr, "LIPO grouping algorithm can only be 0 or 1\n");
               exit (-1);
             }
           __gcov_lipo_grouping_algorithm = ret;
@@ -505,7 +710,7 @@  process_args (int argc, char **argv)
           sscanf (optarg, "%d", &ret);
           if (ret < 1)
             {
-              fprintf (stderr, "LIPO random group size needs to be positive.\n");
+              fnotice (stderr, "LIPO random group size needs to be positive\n");
               exit (-1);
             }
           __gcov_lipo_random_group_size = ret;
@@ -518,7 +723,7 @@  process_args (int argc, char **argv)
           sscanf (optarg, "%d", &ret);
           if (ret < 0)
             {
-              fprintf (stderr, "LIPO max-memory size needs to be positive. \n");
+              fnotice (stderr, "LIPO max-memory size needs to be positive\n");
               exit (-1);
             }
           __gcov_lipo_max_mem = ret;
@@ -527,7 +732,7 @@  process_args (int argc, char **argv)
           sscanf (optarg, "%d", &ret);
           if (ret < 0 || ret > 100)
             {
-              fprintf (stderr, "LIPO cutoff value range is [0, 100]. \n");
+              fnotice (stderr, "LIPO cutoff value range is [0, 100]\n");
               exit (-1);
             }
           __gcov_lipo_cutoff = ret;;
Index: libgcc/dyn-ipa.c
===================================================================
--- libgcc/dyn-ipa.c	(revision 207316)
+++ libgcc/dyn-ipa.c	(working copy)
@@ -182,6 +182,7 @@  enum GROUPING_ALGORITHM
 static int flag_alg_mode;
 static int flag_modu_merge_edges;
 static int flag_weak_inclusion;
+static int flag_use_existing_grouping;
 static gcov_unsigned_t mem_threshold;
 
 /* Returns 0 if no dump is enabled. Returns 1 if text form graph
@@ -364,6 +365,7 @@  static void
 init_dyn_call_graph (void)
 {
   unsigned num_modules = 0;
+  unsigned max_module_id = 0;
   struct gcov_info *gi_ptr;
   const char *env_str;
   int do_dump = (do_cgraph_dump () != 0);
@@ -381,12 +383,22 @@  init_dyn_call_graph (void)
   gi_ptr = gcov_list;
 
   for (; gi_ptr; gi_ptr = gi_ptr->next)
-    num_modules++;
+    {
+      unsigned mod_id = get_module_ident (gi_ptr);
+      num_modules++;
+      if (max_module_id < mod_id)
+        max_module_id = mod_id;
+    }
 
+  if (num_modules < max_module_id)
+    num_modules = max_module_id;
+
   the_dyn_call_graph.num_modules = num_modules;
 
   the_dyn_call_graph.modules
     = XNEWVEC (struct gcov_info *, num_modules);
+  memset (the_dyn_call_graph.modules, 0,
+          num_modules * sizeof (struct gcov_info*));
 
   the_dyn_call_graph.sup_modules
     = XNEWVEC (struct dyn_module_info, num_modules);
@@ -476,13 +488,16 @@  void
 __gcov_finalize_dyn_callgraph (void)
 {
   unsigned i;
-  struct gcov_info *gi_ptr;
 
   for (i = 0; i < the_dyn_call_graph.num_modules; i++)
     {
-      gi_ptr = the_dyn_call_graph.modules[i];
+      struct gcov_info *gi_ptr = the_dyn_call_graph.modules[i];
       const struct gcov_fn_info *fi_ptr;
       unsigned f_ix;
+
+      if (gi_ptr == NULL)
+        continue;
+
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
         {
           struct dyn_cgraph_node *node;
@@ -651,6 +666,8 @@  gcov_build_callgraph (void)
       unsigned f_ix, i;
 
       gi_ptr = the_dyn_call_graph.modules[m_ix];
+      if (gi_ptr == NULL)
+        continue;
 
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
         {
@@ -901,6 +918,8 @@  gcov_compute_cutoff_count (void)
       unsigned f_ix;
 
       gi_ptr = the_dyn_call_graph.modules[m_ix];
+      if (gi_ptr == NULL)
+        continue;
 
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
 	{
@@ -1239,7 +1258,8 @@  gcov_process_cgraph_node (struct dyn_cgraph_node *
             {
               struct gcov_info *callee_mod_info
                   = get_module_info (callee_mod_id);
-              imp_mod_set_insert (imp_modules, callee_mod_info, callee_mod_wt);
+              if (callee_mod_info)
+                imp_mod_set_insert (imp_modules, callee_mod_info, callee_mod_wt);
             }
         }
 
@@ -1629,6 +1649,8 @@  build_modu_graph (gcov_type cutoff_count)
       unsigned f_ix;
 
       gi_ptr = the_dyn_call_graph.modules[m_ix];
+      if (gi_ptr == NULL)
+        continue;
       modu_nodes[m_ix].module = gi_ptr;
 
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
@@ -1950,6 +1972,8 @@  gcov_compute_module_groups_eager_propagation (gcov
       unsigned f_ix;
 
       gi_ptr = the_dyn_call_graph.modules[m_ix];
+      if (gi_ptr == NULL)
+        continue;
 
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
 	{
@@ -1972,6 +1996,8 @@  gcov_compute_module_groups_eager_propagation (gcov
       unsigned f_ix;
 
       gi_ptr = the_dyn_call_graph.modules[m_ix];
+      if (gi_ptr == NULL)
+        continue;
 
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
 	{
@@ -2036,7 +2062,8 @@  gcov_compute_random_module_groups (unsigned max_gr
 	  if (mod_idx == m_ix)
 	    continue;
 	  imp_mod_info = get_module_info (mod_idx + 1);
-	  if (!imp_mod_set_insert (imp_modules, imp_mod_info, 1.0))
+          if (imp_mod_info &&
+	      !imp_mod_set_insert (imp_modules, imp_mod_info, 1.0))
 	    i++;
 	}
     }
@@ -2155,6 +2182,112 @@  gcov_write_module_infos (struct gcov_info *mod_inf
     }
 }
 
+/* Set to use module grouping from existing imports files in
+   the profile directory.  */
+void set_use_existing_grouping (void);
+
+void
+set_use_existing_grouping (void)
+{
+  flag_use_existing_grouping = 1;
+}
+
+#ifdef IN_GCOV_TOOL
+extern const char *get_source_profile_dir (void);
+
+/* find and open the imports files based on da_filename
+   in GI_PTR.  */
+
+static FILE *
+open_imports_file (struct gcov_info *gi_ptr)
+{
+  const char *gcda_name;
+  char *imports_name;
+  const char *source_dir = "";
+
+  if (gi_ptr == NULL || gi_ptr->mod_info == NULL)
+    return NULL;
+
+  gcda_name = gi_ptr->mod_info->da_filename;
+  gcc_assert (gcda_name);
+
+#ifdef IN_GCOV_TOOL
+  source_dir = get_source_profile_dir ();
+  gcc_assert (source_dir);
+#endif
+  imports_name = (char *) alloca (strlen (gcda_name) + strlen (source_dir) +
+                                  strlen (".gcda.imports") + 2);
+  strcpy (imports_name, source_dir);
+  strcat (imports_name, "/");
+  strcat (imports_name, gcda_name);
+  strcat (imports_name, ".gcda.imports");
+  return fopen (imports_name, "r");
+}
+
+extern int get_module_id_from_name (const char *);
+
+#endif /* IN_GCOV_TOOL */
+
+/* Use the module grouping from existing imports files in
+   the profile directory.  */
+
+static void
+read_modu_groups_from_imports_files (void)
+{
+#ifdef IN_GCOV_TOOL
+  unsigned m_ix;
+
+  init_dyn_call_graph ();
+
+  for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+    {
+      struct gcov_info *gi_ptr = the_dyn_call_graph.modules[m_ix];
+      FILE *fd;
+      struct dyn_pointer_set *imp_modules;
+
+      if (gi_ptr == NULL)
+        continue;
+
+      imp_modules = gcov_get_module_imp_module_set
+                      (&the_dyn_call_graph.sup_modules[m_ix]);
+
+      if ((fd = open_imports_file (gi_ptr)) != NULL)
+	{
+          char *line = NULL;
+          size_t linecap = 0;
+          while (getline (&line, &linecap, fd) != -1)
+            {
+              unsigned mod_id = 0;
+              char *name = strtok (line, " \t\n");
+
+              if (name && (mod_id = get_module_id_from_name (name)))
+                {
+                  struct gcov_info *imp_mod_info;
+           	  unsigned mod_idx = mod_id - 1;
+           	  if (mod_idx == m_ix)
+           	    continue;
+           	  imp_mod_info = get_module_info (mod_idx + 1);
+           	  imp_mod_set_insert (imp_modules, imp_mod_info, 1.0);
+                }
+              free (line);
+              line = NULL;
+            }
+          fclose (fd);
+	}
+    }
+
+  /* Now compute the export attribute  */
+  for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+    {
+      struct dyn_module_info *mi
+	= &the_dyn_call_graph.sup_modules[m_ix];
+      if (mi->imported_modules)
+        pointer_set_traverse (mi->imported_modules,
+                              gcov_mark_export_modules, 0, 0, 0);
+    }
+#endif /* IN_GCOV_TOOL */
+}
+
 /* Compute module groups needed for L-IPO compilation.  */
 
 void
@@ -2192,6 +2325,12 @@  __gcov_compute_module_groups (void)
       return;
     }
 
+  if (flag_use_existing_grouping)
+    {
+      read_modu_groups_from_imports_files ();
+      return;
+    }
+
   /* First compute dynamic call graph.  */
   gcov_build_callgraph ();
 
@@ -2333,6 +2472,8 @@  gcov_dump_callgraph (gcov_type cutoff_count)
       unsigned f_ix;
 
       gi_ptr = the_dyn_call_graph.modules[m_ix];
+      if (gi_ptr == NULL)
+        continue;
 
       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
 	{
Index: libgcc/libgcov-util.c
===================================================================
--- libgcc/libgcov-util.c	(revision 207316)
+++ libgcc/libgcov-util.c	(working copy)
@@ -267,6 +267,111 @@  read_gcda_finalize (struct gcov_info *obj_info)
 extern void gcov_read_module_info (struct gcov_module_info *mod_info,
     gcov_unsigned_t len);
 
+/* Substitute string is of this format:
+     old_sub1:new_sub1[,old_sub2:new_sub2]
+   Note that we only apply the substutution ONE time, for the first match.  */
+
+static const char *substitute_string;
+
+/* A global function to set the substitute string.  */
+
+void
+lipo_set_substitute_string (const char *str)
+{
+  char *sub_dup = xstrdup (str);
+  char *cur_sub = sub_dup;
+
+  /* First check if the str is in the right form.  */
+  do
+    {
+      char *new_str;
+      char *next = strchr (cur_sub, ',');
+      if (next)
+        *next++ = '\0';
+      new_str = strchr (cur_sub, ':');
+      if (!new_str)
+        {
+          fprintf (stderr, "Warning: Skip invalid substibution string:%s\n",
+                   str);
+          free (sub_dup);
+          return;
+        }
+      *new_str++ = '\0';
+      cur_sub = next;
+    } while (cur_sub);
+
+  free (sub_dup);
+  substitute_string = str;
+}
+
+/* Replace the first occurance of CUT_STR to NEW_STR in INPUT_STR.  */
+
+static char *
+lipo_process_substitute_string_1 (char *input_str,
+                                  const char *cur_str,
+                                  const char *new_str)
+{
+  char *p;
+
+  if (!input_str || !cur_str || !new_str)
+    return input_str;
+
+  if (cur_str != NULL
+      && (p = strstr (input_str, cur_str)) != NULL)
+    {
+      char *t;
+
+      if (verbose)
+        printf ("Substitute: %s \n", input_str);
+      t = (char*) xmalloc (strlen (input_str) + 1
+          + strlen (new_str) - strlen (cur_str));
+      *p = 0;
+
+      strcpy (t, input_str);
+      strcat (t, new_str);
+      strcat (t, p + strlen (cur_str));
+      if (verbose)
+        printf ("       -->  %s\n", t);
+      return t;
+    }
+
+  return input_str;
+}
+
+/* Parse the substitute string and apply to the INPUT_STR.  */
+
+static char *
+lipo_process_substitute_string (char *input_str)
+{
+  char *sub_dup, *cur_sub, *ret;
+
+  if (substitute_string == NULL)
+    return input_str;
+
+  sub_dup = xstrdup (substitute_string);
+  cur_sub = sub_dup;
+  ret = input_str;
+
+  do
+    {
+      char *new_str, *new_input;
+      char *next = strchr (cur_sub, ',');
+      if (next)
+        *next++ = '\0';
+      new_str = strchr (cur_sub, ':');
+      gcc_assert (new_str);
+      *new_str++ = '\0';
+      new_input = ret;
+      ret = lipo_process_substitute_string_1 (new_input, cur_sub, new_str);
+      if (ret != new_input)
+        free (new_input);
+      cur_sub = next;
+    } while (cur_sub);
+
+  free (sub_dup);
+  return ret;
+}
+
 /* This function reads module_info from a gcda file.  */
 
 static void
@@ -280,7 +385,11 @@  tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
   gcov_read_module_info (mod_info, length);
 
   if (mod_info->is_primary)
-    curr_module_info = mod_info;
+    {
+      mod_info->da_filename =
+          lipo_process_substitute_string (mod_info->da_filename);
+      curr_module_info = mod_info;
+    }
   else
     free (mod_info);
 }
@@ -295,7 +404,8 @@  read_gcda_file (const char *filename)
   unsigned depth = 0;
   unsigned magic, version;
   struct gcov_info *obj_info;
-  int i;
+  int i, len;
+  char *str_dup;
 
   for (i=0; i< GCOV_COUNTERS; i++)
     k_ctrs_mask[i] = 0;
@@ -335,15 +445,12 @@  read_gcda_file (const char *filename)
   curr_fn_info = 0;
   curr_module_info = 0;
 
-  {
-    char *str_dup = (char*) xmalloc (strlen (filename) + 1);
-    int len;
+  str_dup = xstrdup (filename);
+  str_dup = lipo_process_substitute_string (xstrdup (filename));
+  obj_info->filename = str_dup;
 
-    strcpy (str_dup, filename);
-    obj_info->filename = str_dup;
-    if ((len = strlen (filename)) > max_filename_len)
-      max_filename_len = len;
-  }
+  if ((len = strlen (str_dup)) > max_filename_len)
+    max_filename_len = len;
 
   /* Read stamp.  */
   obj_info->stamp = gcov_read_unsigned ();
@@ -420,6 +527,21 @@  read_gcda_file (const char *filename)
   return obj_info;
 }
 
+extern int is_module_available (const char *, unsigned *, int);
+
+/* If only use the modules in the modu_list.  */
+
+static int flag_use_modu_list;
+
+/* Set to use only the modules in the modu_list file.  */
+
+void
+set_use_modu_list (void)
+{
+  flag_use_modu_list = 1;
+}
+
+
 /* This will be called by ftw(). It opens and read a gcda file FILENAME.
    Return a non-zero value to stop the tree walk.  */
 
@@ -450,12 +572,39 @@  ftw_read_file (const char *filename,
 
   obj_info = read_gcda_file (filename);
 
+  if (obj_info->mod_info)
+    {
+      unsigned mod_id = obj_info->mod_info->ident;
+      int create = (flag_use_modu_list ? 0 : 1);
+
+      if (!is_module_available (obj_info->mod_info->source_filename,
+                                &mod_id, create))
+        {
+          if (verbose)
+            fprintf (stderr, "warning: module %s (%d) is not avail\n",
+                     obj_info->mod_info->source_filename, mod_id);
+          return 0;
+        }
+    }
+
   obj_info->next = gcov_info_head;
   gcov_info_head = obj_info;
 
   return 0;
 }
 
+/* Source profile directory name.  */
+
+static const char *source_profile_dir;
+
+/* Return Source profile directory name.  */
+
+const char *
+get_source_profile_dir (void)
+{
+  return source_profile_dir;
+}
+
 /* Initializer for reading a profile dir.  */
 
 static inline void
@@ -490,6 +639,8 @@  gcov_read_profile_dir (const char* dir_name, int r
       fprintf (stderr, "%s is not a directory\n", dir_name);
       return NULL;
     }
+  source_profile_dir = getcwd (NULL, 0);
+
   ftw (".", ftw_read_file, 50);
   ret = chdir (pwd);
   free (pwd);