This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: ldconfig speedup


Hi,

my colleague Michael Schröder improved ldconfig with an incremental mode
using a second cache that records inode number and times of a library
and then skips a library if it notices that the library is unchanged.

His patch adds a second cache below the current two ones.  I'm appending
his patch - and would like to have your comments on it.  I plan to
rework the patch this week and get it ready for inclusion and would
therefore need to know what you think about it.

Also, should we add a third cache - or just increase the CACHE_VERSION
and change the second cache to include the new data?

This patch increased ldconfig in repeated runs significantly - I'll do
proper measurements later,

Andreas

--- ./elf/cache.c.orig	2006-04-07 03:36:30.000000000 +0000
+++ ./elf/cache.c	2006-10-18 15:42:27.000000000 +0000
@@ -32,6 +32,16 @@
 #include <ldconfig.h>
 #include <dl-cache.h>
 
+#define CACHEMAGIC_ID "glibc-ld.so.id.1.0"
+
+struct cache_entry_id
+{
+  uint32_t ino;
+  uint32_t mtime;
+  uint32_t size;
+  uint32_t dev;
+};
+
 struct cache_entry
 {
   char *lib;			/* Library name.  */
@@ -40,12 +50,16 @@ struct cache_entry
   unsigned int osversion;	/* Required OS version.  */
   uint64_t hwcap;		/* Important hardware capabilities.  */
   int bits_hwcap;		/* Number of bits set in hwcap.  */
+  struct cache_entry_id id;	/* unique id of entry */
   struct cache_entry *next;	/* Next entry in list.  */
 };
 
 /* List of all cache entries.  */
 static struct cache_entry *entries;
 
+/* List of all old entries for incremental mode */
+static struct cache_entry *old_entries;
+
 static const char *flag_descr[] =
 { "libc4", "ELF", "libc5", "libc6"};
 
@@ -298,6 +312,10 @@ save_cache (const char *cache_name)
 	++cache_entry_old_count;
     }
 
+  if (opt_format != 0)
+    total_strlen += sizeof(CACHEMAGIC_ID) - 1 +
+                    cache_entry_count * sizeof(struct cache_entry_id);
+
   /* Create the on disk cache structure.  */
   /* First an array for all strings.  */
   strings = (char *)xmalloc (total_strlen);
@@ -399,6 +417,19 @@ save_cache (const char *cache_name)
       && idx_old < cache_entry_old_count)
     file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
 
+  /* Add cache_id block to string space */
+  if (opt_format != 0)
+    {
+      file_entries_new->idblock = str_offset;
+      memcpy(str, CACHEMAGIC_ID, sizeof(CACHEMAGIC_ID) - 1);
+      str += sizeof(CACHEMAGIC_ID) - 1;
+      for (entry = entries; entry != NULL; entry = entry->next)
+	{
+	  memcpy(str, &entry->id, sizeof entry->id);
+	  str += sizeof entry->id;
+	}
+    }
+
   /* Write out the cache.  */
 
   /* Write cache first to a temporary file and rename it later.  */
@@ -473,7 +504,8 @@ save_cache (const char *cache_name)
 /* Add one library to the cache.  */
 void
 add_to_cache (const char *path, const char *lib, int flags,
-	      unsigned int osversion, uint64_t hwcap)
+	      unsigned int osversion, uint64_t hwcap,
+	      struct stat64 *stat_buf)
 {
   struct cache_entry *new_entry, *ptr, *prev;
   char *full_path;
@@ -492,6 +524,10 @@ add_to_cache (const char *path, const ch
   new_entry->osversion = osversion;
   new_entry->hwcap = hwcap;
   new_entry->bits_hwcap = 0;
+  new_entry->id.ino = (uint32_t)stat_buf->st_ino;
+  new_entry->id.mtime = (uint32_t)stat_buf->st_mtime;
+  new_entry->id.size = (uint32_t)stat_buf->st_size;
+  new_entry->id.dev = (uint32_t)stat_buf->st_dev;
 
   /* Count the number of bits set in the masked value.  */
   for (i = 0; (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
@@ -521,3 +557,163 @@ add_to_cache (const char *path, const ch
       prev->next = new_entry;
     }
 }
+
+/* Load old cache to search for unchanged entries. Mostly copied
+   from print_cache. Be paraniod about data checking, as we don't
+   know if the cache is from the right architecture or currupted. */
+
+void
+load_old_cache (const char *cache_name)
+{
+  size_t cache_size;
+  const char *cache_data, *cache_data_id;
+  struct stat64 st;
+  int fd;
+  unsigned int i;
+  size_t j;
+  struct cache_file *cache;
+  struct cache_file_new *cache_new = NULL;
+  struct cache_entry *new_entry;
+  uint64_t hwcap;
+
+  old_entries = NULL;
+  if (!cache_name)
+    return;
+  fd = open (cache_name, O_RDONLY);
+  if (fd < 0)
+    return;
+  if (fstat64 (fd, &st) < 0 || st.st_size == 0)
+    {
+      close (fd);
+      return;
+    }
+  cache = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (cache == MAP_FAILED)
+    {
+      close (fd);
+      return;
+    }
+  cache_size = st.st_size;
+  if (cache_size < sizeof (struct cache_file))
+    {
+      close (fd);
+      return;
+    }
+  cache_new = (struct cache_file_new *)cache;
+  if (!memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
+    {
+      if (cache->nlibs < 0 || cache->nlibs >= cache_size)
+	{
+	  close (fd);
+	  return;
+	}
+      size_t offset = ALIGN_CACHE (sizeof (struct cache_file)
+				   + (cache->nlibs
+				      * sizeof (struct file_entry)));
+      if (cache_size >
+	  (offset + sizeof (struct cache_file_new)))
+	  cache_new = (struct cache_file_new *) ((void *)cache + offset);
+    }
+  if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
+      || memcmp (cache_new->version, CACHE_VERSION,
+		      sizeof CACHE_VERSION - 1))
+    {
+      close (fd);
+      return;
+    }
+  cache_data = (const char *) cache_new;
+  if (cache_new->idblock <= 0)
+    {
+      close (fd);
+      return;
+    }
+  if (cache_new->idblock + (cache_new->nlibs + 1) * sizeof(struct cache_entry_id) > cache_size)
+    {
+      close (fd);
+      return;
+    }
+  cache_data_id = cache_data + cache_new->idblock;
+  if (memcmp(cache_data_id, CACHEMAGIC_ID, sizeof(CACHEMAGIC_ID) - 1))
+    {
+      close (fd);
+      return;
+    }
+  cache_data_id += sizeof(CACHEMAGIC_ID) - 1;
+  memset(&st, 0, sizeof st);
+  for (i = 0; i < cache_new->nlibs; i++)
+    {
+      new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry));
+      new_entry->lib = xstrdup (cache_data + cache_new->libs[i].key);
+      new_entry->path = xstrdup (cache_data + cache_new->libs[i].value);
+      new_entry->flags = cache_new->libs[i].flags;
+      new_entry->osversion = cache_new->libs[i].osversion;
+      hwcap = cache_new->libs[i].hwcap;
+      new_entry->hwcap = hwcap;
+      new_entry->bits_hwcap = 0;
+      memcpy(&new_entry->id, cache_data_id + sizeof(struct cache_entry_id) * i, sizeof(struct cache_entry_id));
+      /* Count the number of bits set in the masked value.  */
+      for (j = 0; (~((1ULL << j) - 1) & hwcap) != 0 && j < 8 * sizeof (hwcap); ++j)
+	if ((hwcap & (1ULL << j)) != 0)
+	  ++new_entry->bits_hwcap;
+      new_entry->next = old_entries;
+      old_entries = new_entry;
+    }
+  close (fd);
+  return;
+}
+
+int
+search_old_cache(const char *file, struct stat64 *stat_buf, int *flags, unsigned int *osversion, char **soname)
+{
+  struct cache_entry *entry;
+  struct cache_entry_id id;
+
+  id.ino = (uint32_t)stat_buf->st_ino;
+  id.mtime = (uint32_t)stat_buf->st_mtime;
+  id.size = (uint32_t)stat_buf->st_size;
+  id.dev = (uint32_t)stat_buf->st_dev;
+  for (entry = old_entries; entry; entry = entry->next)
+    {
+      if (id.ino == entry->id.ino &&
+          id.mtime == entry->id.mtime &&
+          id.size == entry->id.size &&
+          id.dev == entry->id.dev)
+	{
+#if 0
+	  const char *p1, *p2;
+	  /* basename check for extra safety */
+	  p1 = strrchr(entry->path, '/');
+	  if (!p1)
+	    p1 = entry->path;
+	  else
+	    p1++;
+	  for (p2 = p1; *p2; p2++)
+	    if (*p2 == '.' || *p2 == '-')
+	      break;
+	  if (p2 != p1 && strncmp(file, p1, p2 - p1))
+	    return 0;
+#endif
+	  *flags = entry->flags;
+	  *osversion = entry->osversion;
+	  *soname = xstrdup(entry->lib);
+	  return 1;
+	}
+    }
+  return 0;
+}
+
+void
+free_old_cache(void)
+{
+  struct cache_entry *entry;
+
+  while (old_entries)
+    {
+      entry = old_entries;
+      free (entry->path);
+      free (entry->lib);
+      old_entries = entry->next;
+      free (entry);
+    }
+}
+
--- ./elf/ldconfig.c.orig	2006-04-07 06:57:49.000000000 +0000
+++ ./elf/ldconfig.c	2006-10-18 15:43:23.000000000 +0000
@@ -122,6 +122,9 @@ static unsigned long int hwcap_mask = HW
 /* Configuration-defined capabilities defined in kernel vDSOs.  */
 static const char *hwcap_extra[64 - _DL_FIRST_EXTRA];
 
+/* Should we ignore an old cache file? */
+static int opt_ignore_old_cache;
+
 /* Name and version of program.  */
 static void print_version (FILE *stream, struct argp_state *state);
 void (*argp_program_version_hook) (FILE *, struct argp_state *)
@@ -140,6 +143,7 @@ static const struct argp_option options[
   { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line.  Don't build cache."), 0},
   { NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0},
   { "format", 'c', N_("FORMAT"), 0, N_("Format to use: new, old or compat (default)"), 0},
+  { "ignore-old-cache", 'i', NULL, 0, N_("Ignore old cache file"), 0},
   { NULL, 0, NULL, 0, NULL, 0 }
 };
 
@@ -270,6 +274,9 @@ parse_opt (int key, char *arg, struct ar
       else if (strcmp (arg, "new") == 0)
 	opt_format = 2;
       break;
+    case 'i':
+      opt_ignore_old_cache = 1;
+      break;
     default:
       return ARGP_ERR_UNKNOWN;
     }
@@ -571,7 +578,7 @@ manual_link (char *library)
       return;
     }
   if (process_file (real_library, library, libname, &flag, &osversion,
-		    &soname, 0))
+		    &soname, 0, NULL))
     {
       error (0, 0, _("No link created since soname could not be found for %s"),
 	     library);
@@ -616,6 +623,7 @@ struct dlib_entry
   int flag;
   int is_link;
   unsigned int osversion;
+  struct stat64 stat_buf;
   struct dlib_entry *next;
 };
 
@@ -631,7 +639,7 @@ search_dir (const struct dir_entry *entr
   struct dlib_entry *dlibs;
   struct dlib_entry *dlib_ptr;
   struct stat64 lstat_buf, stat_buf;
-  int is_link, is_dir;
+  int is_link, is_dir, has_soname;
   uint64_t hwcap = path_hwcap (entry->path);
   unsigned int osversion;
 
@@ -725,16 +733,11 @@ search_dir (const struct dir_entry *entr
 	    }
 	  sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name);
 	}
-#ifdef _DIRENT_HAVE_D_TYPE
-      if (direntry->d_type != DT_UNKNOWN)
-	lstat_buf.st_mode = DTTOIF (direntry->d_type);
-      else
-#endif
-	if (__builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
-	  {
-	    error (0, errno, _("Cannot lstat %s"), file_name);
-	    continue;
-	  }
+      if (__builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
+	{
+	  error (0, errno, _("Cannot lstat %s"), file_name);
+	  continue;
+	}
 
       is_link = S_ISLNK (lstat_buf.st_mode);
       if (is_link)
@@ -752,6 +755,10 @@ search_dir (const struct dir_entry *entr
 	      continue;
 	    }
 	  is_dir = S_ISDIR (stat_buf.st_mode);
+          lstat_buf.st_ino = stat_buf.st_ino;
+          lstat_buf.st_mtime = stat_buf.st_mtime;
+          lstat_buf.st_size = stat_buf.st_size;
+          lstat_buf.st_dev = stat_buf.st_dev;
 	}
       else
 	is_dir = S_ISDIR (lstat_buf.st_mode);
@@ -772,20 +779,6 @@ search_dir (const struct dir_entry *entr
 	    }
 	  else
 	    {
-#ifdef _DIRENT_HAVE_D_TYPE
-	      /* We have filled in lstat only #ifndef
-		 _DIRENT_HAVE_D_TYPE.  Fill it in if needed.  */
-	      if (direntry->d_type != DT_UNKNOWN
-		  && __builtin_expect (lstat64 (real_file_name, &lstat_buf),
-				       0))
-		{
-		  error (0, errno, _("Cannot lstat %s"), file_name);
-		  free (new_entry->path);
-		  free (new_entry);
-		  continue;
-		}
-#endif
-
 	      new_entry->ino = lstat_buf.st_ino;
 	      new_entry->dev = lstat_buf.st_dev;
 	    }
@@ -808,15 +801,18 @@ search_dir (const struct dir_entry *entr
       else
 	real_name = real_file_name;
 
-      if (process_file (real_name, file_name, direntry->d_name, &flag,
-			&osversion, &soname, is_link))
+      has_soname = 1;
+      if (!search_old_cache(direntry->d_name, &lstat_buf, &flag, &osversion, &soname))
 	{
-	  if (real_name != real_file_name)
-	    free (real_name);
-	  continue;
+	  if (process_file (real_name, file_name, direntry->d_name, &flag,
+			    &osversion, &soname, is_link, &has_soname))
+	    {
+	      if (real_name != real_file_name)
+		free (real_name);
+	      continue;
+	    }
 	}
 
-
       /* A link may just point to itself.  */
       if (is_link)
 	{
@@ -839,6 +835,8 @@ search_dir (const struct dir_entry *entr
 
       if (is_link)
 	{
+	  if (strcmp(soname, direntry->d_name) != 0)
+	    has_soname = 0;
 	  free (soname);
 	  soname = xstrdup (direntry->d_name);
 	}
@@ -846,7 +844,21 @@ search_dir (const struct dir_entry *entr
       if (flag == FLAG_ELF
 	  && (entry->flag == FLAG_ELF_LIBC5
 	      || entry->flag == FLAG_ELF_LIBC6))
-	flag = entry->flag;
+	{
+	  flag = entry->flag;
+	  /* invalidate id data as we changed flag */
+          has_soname = 0;
+	}
+
+      if (!has_soname)
+	{
+	  /* soname or flags depend on file name of dir properties,
+             so invalidate id entry */
+	  lstat_buf.st_ino = 0;
+	  lstat_buf.st_mtime = 0;
+	  lstat_buf.st_size = 0;
+	  lstat_buf.st_dev = 0;
+	}
       /* Some sanity checks to print warnings.  */
       if (opt_verbose)
 	{
@@ -885,13 +897,20 @@ search_dir (const struct dir_entry *entr
 			       && flag == FLAG_ELF)
 			dlib_ptr->flag = flag;
 		      else
-			error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
-			       dlib_ptr->name, direntry->d_name, entry->path);
+			{
+			  error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
+				 dlib_ptr->name, direntry->d_name, entry->path);
+			  lstat_buf.st_ino = 0;
+			  lstat_buf.st_mtime = 0;
+			  lstat_buf.st_size = 0;
+			  lstat_buf.st_dev = 0;
+			}
 		    }
 		  free (dlib_ptr->name);
 		  dlib_ptr->osversion = osversion;
 		  dlib_ptr->name = xstrdup (direntry->d_name);
 		  dlib_ptr->is_link = is_link;
+		  dlib_ptr->stat_buf = lstat_buf;
 		}
 	      /* Don't add this library, abort loop.  */
 	      /* Also free soname, since it's dynamically allocated.  */
@@ -908,6 +927,7 @@ search_dir (const struct dir_entry *entr
 	  dlib_ptr->osversion = osversion;
 	  dlib_ptr->soname = soname;
 	  dlib_ptr->is_link = is_link;
+	  dlib_ptr->stat_buf = lstat_buf;
 	  /* Add at head of list.  */
 	  dlib_ptr->next = dlibs;
 	  dlibs = dlib_ptr;
@@ -926,7 +946,7 @@ search_dir (const struct dir_entry *entr
 		      dlib_ptr->soname);
       if (opt_build_cache)
 	add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag,
-		      dlib_ptr->osversion, hwcap);
+		      dlib_ptr->osversion, hwcap, &dlib_ptr->stat_buf);
     }
 
   /* Free all resources.  */
@@ -1288,8 +1308,12 @@ main (int argc, char **argv)
 	add_system_dir (LIBDIR);
     }
 
+  load_old_cache(opt_ignore_old_cache ? NULL : cache_file);
+
   search_dirs ();
 
+  free_old_cache();
+
   if (opt_build_cache)
     save_cache (cache_file);
 
--- ./elf/readelflib.c.orig	2005-12-14 10:05:56.000000000 +0000
+++ ./elf/readelflib.c	2006-10-18 09:44:04.000000000 +0000
@@ -213,7 +213,7 @@ process_elf_file (const char *file_name,
   /* We reach this point only if the file doesn't contain a DT_SONAME
      or if we can't classify the library.  If it doesn't have a
      soname, return the name of the library.  */
-  if (*soname == NULL)
+  if (*soname == NULL && lib != NULL)
     *soname = xstrdup (lib);
 
   return 0;
--- ./elf/readlib.c.orig	2005-12-21 22:16:20.000000000 +0000
+++ ./elf/readlib.c	2006-10-18 09:48:36.000000000 +0000
@@ -68,7 +68,7 @@ static struct known_names known_libs[] =
 int
 process_file (const char *real_file_name, const char *file_name,
 	      const char *lib, int *flag, unsigned int *osversion,
-	      char **soname, int is_link)
+	      char **soname, int is_link, int *has_soname)
 {
   FILE *file;
   struct stat64 statbuf;
@@ -80,6 +80,8 @@ process_file (const char *real_file_name
   ret = 0;
   *flag = FLAG_ANY;
   *soname = NULL;
+  if (has_soname)
+    *has_soname = 0;
 
   file = fopen (real_file_name, "rb");
   if (file == NULL)
@@ -165,9 +167,16 @@ process_file (const char *real_file_name
   /* Libraries have to be shared object files.  */
   else if (elf_header->e_type != ET_DYN)
     ret = 1;
-  else if (process_elf_file (file_name, lib, flag, osversion, soname,
+  else
+    {
+      if (process_elf_file (file_name, NULL, flag, osversion, soname,
 			     file_contents, statbuf.st_size))
-    ret = 1;
+	ret = 1;
+      else if (!*soname)
+	*soname = xstrdup(lib);
+      else if (has_soname)
+	*has_soname = 1;
+    }
 
  done:
   /* Clean up allocated memory and resources.  */
--- ./sysdeps/generic/dl-cache.h.orig	2003-06-25 08:01:22.000000000 +0000
+++ ./sysdeps/generic/dl-cache.h	2006-10-17 18:58:08.000000000 +0000
@@ -90,7 +90,8 @@ struct cache_file_new
   char version[sizeof CACHE_VERSION - 1];
   uint32_t nlibs;		/* Number of entries.  */
   uint32_t len_strings;		/* Size of string table. */
-  uint32_t unused[5];		/* Leave space for future extensions
+  uint32_t idblock;             /* Id block index in string table */
+  uint32_t unused[4];		/* Leave space for future extensions
 				   and align to 8 byte boundary.  */
   struct file_entry_new libs[0]; /* Entries describing libraries.  */
   /* After this the string table of size len_strings is found.	*/
--- ./sysdeps/generic/ldconfig.h.orig	2003-03-14 05:32:49.000000000 +0000
+++ ./sysdeps/generic/ldconfig.h	2006-10-18 10:28:30.000000000 +0000
@@ -43,12 +43,21 @@ extern void init_cache (void);
 extern void save_cache (const char *cache_name);
 
 extern void add_to_cache (const char *path, const char *lib, int flags,
-			  unsigned int osversion, uint64_t hwcap);
+			  unsigned int osversion, uint64_t hwcap,
+			  struct stat64 *stat_buf);
+
+extern void load_old_cache (const char *cache_name);
+
+extern int search_old_cache (const char *file, struct stat64 *stat_buf,
+			     int *flags, unsigned int *osversion,
+			     char **soname);
+
+extern void free_old_cache (void);
 
 /* Declared in readlib.c.  */
 extern int process_file (const char *real_file_name, const char *file_name,
 			 const char *lib, int *flag, unsigned int *osversion,
-			 char **soname, int is_link);
+			 char **soname, int is_link, int *has_soname);
 
 /* Declared in readelflib.c.  */
 extern int process_elf_file (const char *file_name, const char *lib, int *flag,

-- 
 Andreas Jaeger, Director Platform / openSUSE, aj@suse.de
  SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg)
   Maxfeldstr. 5, 90409 Nürnberg, Germany
    GPG fingerprint = 93A3 365E CE47 B889 DF7F  FED1 389A 563C C272 A126

Attachment: pgp00000.pgp
Description: PGP signature


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]