bug 623999 - Takes 35s to start up r=dougt f=taras a=blocking-fennec
authorBrad Lassey <blassey@mozilla.com>
Wed, 19 Jan 2011 17:32:58 -0500
changeset 60884 6643235b98685dd6772000cff268a5d5ad2198fe
parent 60883 6b5a47683b8829e8cf985ed4e4fbe1d2a88bfcc9
child 60885 9d41d0fa20ad757132d72a17cf0d3788b35f5c62
push id18153
push userblassey@mozilla.com
push dateWed, 19 Jan 2011 22:37:53 +0000
treeherdermozilla-central@6643235b9868 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt, blocking-fennec
bugs623999
milestone2.0b10pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 623999 - Takes 35s to start up r=dougt f=taras a=blocking-fennec
other-licenses/android/APKOpen.cpp
other-licenses/android/APKOpen.h
other-licenses/android/dlfcn.c
--- a/other-licenses/android/APKOpen.cpp
+++ b/other-licenses/android/APKOpen.cpp
@@ -189,36 +189,16 @@ find_cdir_entry (struct cdir_entry *entr
     if (letoh16(entry->filename_size) == name_size &&
         !memcmp(entry->data, name, name_size))
       return entry;
     entry = (struct cdir_entry *)((void *)entry + cdir_entry_size(entry));
   }
   return NULL;
 }
 
-static uint32_t simple_write(int fd, const void *buf, uint32_t count)
-{
-  uint32_t out_offset = 0;
-  while (out_offset < count) {
-    uint32_t written = write(fd, buf + out_offset,
-                             count - out_offset);
-    if (written == -1) {
-      if (errno == EAGAIN || errno == EWOULDBLOCK)
-        continue;
-      else {
-        __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "simple_write failed");
-        break;
-      }
-    }
-
-    out_offset += written;
-  }
-  return out_offset;
-}
-
 #define SHELL_WRAPPER0(name) \
 typedef void (*name ## _t)(JNIEnv *, jclass); \
 static name ## _t f_ ## name; \
 extern "C" NS_EXPORT void JNICALL \
 Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc) \
 { \
   f_ ## name(jenv, jc); \
 }
@@ -262,16 +242,41 @@ SHELL_WRAPPER1(removeObserver, jstring)
 static void * xul_handle = NULL;
 static time_t apk_mtime = 0;
 #ifdef DEBUG
 extern "C" int extractLibs = 1;
 #else
 extern "C" int extractLibs = 0;
 #endif
 
+#ifdef DEBUG
+#define DEBUG_EXTRACT_LIBS 1
+#endif
+
+#ifdef DEBUG_EXTRACT_LIBS
+static uint32_t simple_write(int fd, const void *buf, uint32_t count)
+{
+  uint32_t out_offset = 0;
+  while (out_offset < count) {
+    uint32_t written = write(fd, buf + out_offset,
+                             count - out_offset);
+    if (written == -1) {
+      if (errno == EAGAIN || errno == EWOULDBLOCK)
+        continue;
+      else {
+        __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "simple_write failed");
+        break;
+      }
+    }
+
+    out_offset += written;
+  }
+  return out_offset;
+}
+
 static void
 extractFile(const char * path, const struct cdir_entry *entry, void * data)
 {
   uint32_t size = letoh32(entry->uncompressed_size);
 
   struct stat status;
   if (!stat(path, &status) &&
       status.st_size == size &&
@@ -321,16 +326,17 @@ extractFile(const char * path, const str
 
   ret = inflateEnd(&strm);
   if (ret != Z_OK)
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflateEnd failed: %s", strm.msg);
 
   close(fd);
   munmap(buf, 4096);
 }
+#endif
 
 static void
 extractLib(const struct cdir_entry *entry, void * data, void * dest)
 {
   z_stream strm = {
     next_in: (Bytef *)data,
     avail_in: letoh32(entry->compressed_size),
     total_in: 0,
@@ -417,38 +423,41 @@ lookupLibCacheFd(const char *libName)
     struct lib_cache_info *info = &cache_mapping[count];
     if (!strcmp(libName, info->name))
       return info->fd;
   }
   return -1;
 }
 
 static void
-addLibCacheFd(const char *libName, int fd)
+addLibCacheFd(const char *libName, int fd, uint32_t lib_size = 0, void* buffer = NULL)
 {
   ensureLibCache();
 
   struct lib_cache_info *info = &cache_mapping[cache_count++];
   strncpy(info->name, libName, MAX_LIB_CACHE_NAME_LEN - 1);
   info->fd = fd;
+  info->lib_size = lib_size;
+  info->buffer = buffer;
 }
 
 static void * mozload(const char * path, void *zip,
                       struct cdir_entry *cdir_start, uint16_t cdir_entries)
 {
 #ifdef DEBUG
   struct timeval t0, t1;
   gettimeofday(&t0, 0);
 #endif
 
   struct cdir_entry *entry = find_cdir_entry(cdir_start, cdir_entries, path);
   struct local_file_header *file = (struct local_file_header *)(zip + letoh32(entry->offset));
   void * data = ((void *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
   void * handle;
 
+#ifdef DEBUG_EXTRACT_LIBS
   if (extractLibs) {
     char fullpath[256];
     snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), path + 4);
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
     extractFile(fullpath, entry, data);
     handle = __wrap_dlopen(fullpath, RTLD_LAZY);
     if (!handle)
       __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", fullpath, __wrap_dlerror());
@@ -457,34 +466,40 @@ static void * mozload(const char * path,
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path,
                         (((long long)t1.tv_sec * 1000000LL) +
                           (long long)t1.tv_usec) -
                         (((long long)t0.tv_sec * 1000000LL) +
                           (long long)t0.tv_usec));
 #endif
     return handle;
   }
-
+#endif
   size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
 
   int fd = zip_fd;
   void * buf = NULL;
   uint32_t lib_size = letoh32(entry->uncompressed_size);
+  int cache_fd = 0;
   if (letoh16(file->compression) == DEFLATE) {
-    int cache_fd = lookupLibCacheFd(path + 4);
+    cache_fd = lookupLibCacheFd(path + 4);
     fd = cache_fd;
     if (fd < 0) {
       char fullpath[256];
       snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), path + 4);
       fd = open(fullpath, O_RDWR);
       struct stat status;
       if (stat(fullpath, &status) ||
           status.st_size != lib_size ||
-          apk_mtime > status.st_mtime)
+          apk_mtime > status.st_mtime) {
+        unlink(fullpath);
         fd = -1;
+      } else {
+        cache_fd = fd;
+        addLibCacheFd(path + 4, fd);
+      }
     }
     if (fd < 0)
       fd = createAshmem(lib_size, path);
 #ifdef DEBUG
     else
       __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s from cache", path + 4);
 #endif
     if (fd < 0) {
@@ -499,30 +514,36 @@ static void * mozload(const char * path,
       close(fd);
       return NULL;
     }
 
     offset = 0;
 
     if (cache_fd < 0) {
       extractLib(entry, data, buf);
-      addLibCacheFd(path + 4, fd);
+      addLibCacheFd(path + 4, fd, lib_size, buf);
     }
+    // preload libxul, to avoid slowly demand-paging it
+    if (!strcmp(path, "lib/libxul.so"))
+      madvise(buf, entry->uncompressed_size, MADV_WILLNEED);
     data = buf;
   }
 
 #ifdef DEBUG
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s with len %d (0x%08x) and offset %d (0x%08x)", path, lib_size, lib_size, offset, offset);
 #endif
 
   handle = moz_mapped_dlopen(path, RTLD_LAZY, fd, data,
                              lib_size, offset);
   if (!handle)
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", path, __wrap_dlerror());
-  if (buf)
+
+  // if we're extracting the libs to disk and cache_fd is not valid then 
+  // keep this buffer around so it can be used to write to disk
+  if (buf && (!extractLibs || cache_fd >= 0))
     munmap(buf, lib_size);
 
 #ifdef DEBUG
   gettimeofday(&t1, 0);
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path, (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) -
                (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec));
 #endif
 
@@ -665,16 +686,46 @@ Java_org_mozilla_gecko_GeckoAppShell_loa
   // XXX: java doesn't give us true UTF8, we should figure out something 
   // better to do here
   str = jenv->GetStringUTFChars(jApkName, NULL);
   if (str == NULL)
     return;
 
   loadLibs(str);
   jenv->ReleaseStringUTFChars(jApkName, str);
+  if (extractLibs && cache_mapping) {
+    if (!fork()) {
+      sleep(10);
+      nice(10);
+      int count = cache_count;
+      while (count--) {
+        struct lib_cache_info *info = &cache_mapping[count];
+        if (!info->buffer)
+          continue;
+
+        char fullpath[256];
+        snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), info->name);
+        char tmp_path[256];
+        sprintf(tmp_path, "%s.tmp", fullpath);
+        int file_fd = open(tmp_path, O_CREAT | O_WRONLY);
+        // using sendfile would be preferable, but it doesn't seem to work
+        // with shared memory on any of the devices we've tested
+        uint32_t sent = write(file_fd, info->buffer, info->lib_size);
+
+        munmap(info->buffer, info->lib_size);
+        info->buffer = 0;
+        close(file_fd);
+        if (sent == info->lib_size)
+          rename(tmp_path, fullpath);
+        else
+          unlink(tmp_path);
+      }
+      exit(0);
+    }
+  }
 }
 
 typedef int GeckoProcessType;
 typedef int nsresult;
 
 extern "C" NS_EXPORT int
 ChildProcessInit(int argc, char* argv[])
 {
--- a/other-licenses/android/APKOpen.h
+++ b/other-licenses/android/APKOpen.h
@@ -48,13 +48,15 @@ struct mapping_info {
 const struct mapping_info * getLibraryMapping();
 
 #define MAX_LIB_CACHE_ENTRIES 32
 #define MAX_LIB_CACHE_NAME_LEN 32
 
 struct lib_cache_info {
   char name[MAX_LIB_CACHE_NAME_LEN];
   int fd;
+  uint32_t lib_size;
+  void* buffer;
 };
 
 const struct lib_cache_info * getLibraryCache();
 
 #endif /* APKOpen_h */
--- a/other-licenses/android/dlfcn.c
+++ b/other-licenses/android/dlfcn.c
@@ -49,18 +49,20 @@ static void set_dlerror(int err)
 {
     format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s: %s", dl_errors[err],
              linker_get_error());
     dl_err_str = (const char *)&dl_err_buf[0];
 }
 
 void *__wrap_dlopen(const char *filename, int flag)
 {
+#ifdef DEBUG
     if (extractLibs)
         return dlopen(filename, flag);
+#endif
 
     soinfo *ret;
 
     pthread_mutex_lock(&dl_lock);
     ret = find_library(filename);
     if (unlikely(ret == NULL)) {
         set_dlerror(DL_ERR_CANNOT_LOAD_LIBRARY);
     } else {