dlopen the bundle's executable before calling -[NSBundle load] since that will also...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Apr 2018 03:33:58 +0000 (03:33 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Apr 2018 03:33:58 +0000 (03:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184904

Reviewed by Geoffrey Garen.

Loading an NSBundle does a lot of work to find the principal class inside
the bundle. This means it walks all the objective C class names loaded
by the bundle. Doing this is *really* expensive.

Some users of the injected bundle define a WKBundleInitialize function.
In such a case, we don't need the principal class, so we can skip loading
the NSBundle. Now, before we load the bundle, we dlopen and dlsym looking
for the WKBundleInitialize function. If we find it, we skip loading
the bundle. If we don't find the WKBundleInitialize function, we fall
back to loading the bundle and finding the principal class.

This speeds up initializeWebProcess by ~70ms on my MBP.

* WebProcess/InjectedBundle/mac/InjectedBundleMac.mm:
(WebKit::InjectedBundle::initialize):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@231041 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/ChangeLog
Source/WebKit/WebProcess/InjectedBundle/mac/InjectedBundleMac.mm

index f58a3e7..8a1dd17 100644 (file)
@@ -1,3 +1,26 @@
+2018-04-25  Saam Barati  <sbarati@apple.com>
+
+        dlopen the bundle's executable before calling -[NSBundle load] since that will also do a bunch of other things we don't need
+        https://bugs.webkit.org/show_bug.cgi?id=184904
+
+        Reviewed by Geoffrey Garen.
+
+        Loading an NSBundle does a lot of work to find the principal class inside
+        the bundle. This means it walks all the objective C class names loaded
+        by the bundle. Doing this is *really* expensive.
+        
+        Some users of the injected bundle define a WKBundleInitialize function.
+        In such a case, we don't need the principal class, so we can skip loading
+        the NSBundle. Now, before we load the bundle, we dlopen and dlsym looking
+        for the WKBundleInitialize function. If we find it, we skip loading
+        the bundle. If we don't find the WKBundleInitialize function, we fall
+        back to loading the bundle and finding the principal class.
+        
+        This speeds up initializeWebProcess by ~70ms on my MBP.
+
+        * WebProcess/InjectedBundle/mac/InjectedBundleMac.mm:
+        (WebKit::InjectedBundle::initialize):
+
 2018-04-25  Youenn Fablet  <youenn@apple.com>
 
         Use NetworkLoadChecker for all subresource loads except fetch/XHR
index 9056c99..456907e 100644 (file)
@@ -33,7 +33,9 @@
 #import "WKWebProcessBundleParameters.h"
 #import "WKWebProcessPlugInInternal.h"
 #import "WebProcessCreationParameters.h"
+#import <CoreFoundation/CFURL.h>
 #import <Foundation/NSBundle.h>
+#import <dlfcn.h>
 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
 #import <stdio.h>
 #import <wtf/RetainPtr.h>
@@ -76,10 +78,23 @@ bool InjectedBundle::initialize(const WebProcessCreationParameters& parameters,
         WTFLogAlways("InjectedBundle::load failed - Could not create the bundle.\n");
         return false;
     }
+
+    WKBundleInitializeFunctionPtr initializeFunction = nullptr;
+    if (RetainPtr<CFURLRef> executableURL = adoptCF(CFBundleCopyExecutableURL([m_platformBundle _cfBundle]))) {
+        static constexpr size_t maxPathSize = 4096;
+        char pathToExecutable[maxPathSize];
+        if (CFURLGetFileSystemRepresentation(executableURL.get(), true, bitwise_cast<uint8_t*>(pathToExecutable), maxPathSize)) {
+            // We don't hold onto this handle anywhere more permanent since we never dlcose.
+            if (void* handle = dlopen(pathToExecutable, RTLD_LAZY | RTLD_GLOBAL | RTLD_FIRST))
+                initializeFunction = bitwise_cast<WKBundleInitializeFunctionPtr>(dlsym(handle, "WKBundleInitialize"));
+        }
+    }
         
-    if (![m_platformBundle load]) {
-        WTFLogAlways("InjectedBundle::load failed - Could not load the executable from the bundle.\n");
-        return false;
+    if (!initializeFunction) {
+        if (![m_platformBundle load]) {
+            WTFLogAlways("InjectedBundle::load failed - Could not load the executable from the bundle.\n");
+            return false;
+        }
     }
 
 #if WK_API_ENABLED
@@ -100,9 +115,11 @@ bool InjectedBundle::initialize(const WebProcessCreationParameters& parameters,
         m_bundleParameters = adoptNS([[WKWebProcessBundleParameters alloc] initWithDictionary:dictionary]);
     }
 #endif
+    
+    if (!initializeFunction)
+        initializeFunction = bitwise_cast<WKBundleInitializeFunctionPtr>(CFBundleGetFunctionPointerForName([m_platformBundle _cfBundle], CFSTR("WKBundleInitialize")));
 
     // First check to see if the bundle has a WKBundleInitialize function.
-    WKBundleInitializeFunctionPtr initializeFunction = reinterpret_cast<WKBundleInitializeFunctionPtr>(CFBundleGetFunctionPointerForName([m_platformBundle _cfBundle], CFSTR("WKBundleInitialize")));
     if (initializeFunction) {
         initializeFunction(toAPI(this), toAPI(initializationUserData));
         return true;