Bug 1361900: Part 2 - Add process types field to cached script data. r?erahm draft
authorKris Maglione <maglione.k@gmail.com>
Wed, 03 May 2017 17:21:31 -0700
changeset 575177 e54e84924097578d241dfc8a93f0953426f997cb
parent 575176 1b61b2b0ad010c0b0f85cb0e4438da476b97fdfc
child 575178 05b18b96f0e2083d14aaa8ef570e80dcbb1c8b02
push id57985
push usermaglione.k@gmail.com
push dateWed, 10 May 2017 03:01:16 +0000
reviewerserahm
bugs1361900
milestone55.0a1
Bug 1361900: Part 2 - Add process types field to cached script data. r?erahm MozReview-Commit-ID: Gvh672XD0ar
js/xpconnect/loader/ScriptPreloader-inl.h
js/xpconnect/loader/ScriptPreloader.cpp
js/xpconnect/loader/ScriptPreloader.h
js/xpconnect/loader/moz.build
--- a/js/xpconnect/loader/ScriptPreloader-inl.h
+++ b/js/xpconnect/loader/ScriptPreloader-inl.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ScriptPreloader_inl_h
 #define ScriptPreloader_inl_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/EnumSet.h"
 #include "mozilla/Range.h"
 #include "mozilla/Result.h"
 #include "mozilla/Unused.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include <prio.h>
 
@@ -51,16 +52,32 @@ public:
     write(size_t size)
     {
         auto buf = data.AppendElements(size);
         cursor_ += size;
         return buf;
     }
 
     void
+    codeUint8(const uint8_t& val)
+    {
+        *write(sizeof val) = val;
+    }
+
+    template<typename T>
+    void
+    codeUint8(const EnumSet<T>& val)
+    {
+        // EnumSets are always represented as uint32_t values, so we need to
+        // assert that the value actually fits in a uint8 before writing it.
+        uint32_t value = val.serialize();
+        codeUint8(CheckedUint8(value).value());
+    }
+
+    void
     codeUint16(const uint16_t& val)
     {
         LittleEndian::writeUint16(write(sizeof val), val);
     }
 
     void
     codeUint32(const uint32_t& val)
     {
@@ -101,16 +118,36 @@ public:
         MOZ_ASSERT(checkCapacity(size));
 
         auto buf = &data[cursor_];
         cursor_ += size;
         return buf;
     }
 
     bool
+    codeUint8(uint8_t& val)
+    {
+        if (checkCapacity(sizeof val)) {
+            val = *read(sizeof val);
+        }
+        return !error_;
+    }
+
+    template<typename T>
+    bool
+    codeUint8(EnumSet<T>& val)
+    {
+        uint8_t value;
+        if (codeUint8(value)) {
+            val.deserialize(value);
+        }
+        return !error_;
+    }
+
+    bool
     codeUint16(uint16_t& val)
     {
         if (checkCapacity(sizeof val)) {
             val = LittleEndian::readUint16(read(sizeof val));
         }
         return !error_;
     }
 
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -8,16 +8,18 @@
 #include "ScriptPreloader-inl.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Services.h"
 #include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "MainThreadUtils.h"
 #include "nsDebug.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
 #include "nsJSUtils.h"
@@ -36,16 +38,19 @@ namespace {
 static LazyLogModule gLog("ScriptPreloader");
 
 #define LOG(level, ...) MOZ_LOG(gLog, LogLevel::level, (__VA_ARGS__))
 }
 
 using mozilla::dom::AutoJSAPI;
 using namespace mozilla::loader;
 
+ProcessType ScriptPreloader::sProcessType;
+
+
 nsresult
 ScriptPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
                                 nsISupports* aData, bool aAnonymize)
 {
     MOZ_COLLECT_REPORT(
         "explicit/script-preloader/heap/saved-scripts", KIND_HEAP, UNITS_BYTES,
         SizeOfLinkedList(mSavedScripts, MallocSizeOf),
         "Memory used to hold the scripts which have been executed in this "
@@ -80,16 +85,25 @@ ScriptPreloader::GetSingleton()
         singleton = new ScriptPreloader();
         ClearOnShutdown(&singleton);
     }
 
     return *singleton;
 }
 
 
+ProcessType
+ScriptPreloader::GetChildProcessType(const nsAString& remoteType)
+{
+    if (remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
+        return ProcessType::Extension;
+    }
+    return ProcessType::Web;
+}
+
 namespace {
 
 struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
 {
     AutoSafeJSAPI() { Init(); }
 };
 
 
@@ -115,16 +129,22 @@ ScriptPreloader::Trace(JSTracer* trc)
     }
 }
 
 
 ScriptPreloader::ScriptPreloader()
   : mMonitor("[ScriptPreloader.mMonitor]")
   , mSaveMonitor("[ScriptPreloader.mSaveMonitor]")
 {
+    if (XRE_IsParentProcess()) {
+        sProcessType = ProcessType::Parent;
+    } else {
+        sProcessType = GetChildProcessType(dom::ContentChild::GetSingleton()->GetRemoteType());
+    }
+
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     MOZ_RELEASE_ASSERT(obs);
     obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
     obs->AddObserver(this, SHUTDOWN_TOPIC, false);
     obs->AddObserver(this, CLEANUP_TOPIC, false);
     obs->AddObserver(this, CACHE_FLUSH_TOPIC, false);
 
     AutoSafeJSAPI jsapi;
@@ -236,17 +256,17 @@ ScriptPreloader::GetCacheFile(const char
     NS_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache")));
     Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
 
     NS_TRY(cacheFile->AppendNative(nsDependentCString(leafName)));
 
     return Move(cacheFile);
 }
 
-static const uint8_t MAGIC[] = "mozXDRcache";
+static const uint8_t MAGIC[] = "mozXDRcachev001";
 
 Result<Ok, nsresult>
 ScriptPreloader::OpenCache()
 {
     NS_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD)));
 
     nsCOMPtr<nsIFile> cacheFile;
     MOZ_TRY_VAR(cacheFile, GetCacheFile("scriptCache.bin"));
@@ -348,17 +368,18 @@ ScriptPreloader::InitCache()
     MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
     JSContext* cx = jsapi.cx();
 
     auto start = TimeStamp::Now();
     LOG(Info, "Off-thread decoding scripts...\n");
 
     JS::CompileOptions options(cx, JSVERSION_LATEST);
     for (auto script : mRestoredScripts) {
-        if (script->mSize > MIN_OFFTHREAD_SIZE &&
+        if (script->mProcessTypes.contains(CurrentProcessType()) &&
+            script->mSize > MIN_OFFTHREAD_SIZE &&
             JS::CanCompileOffThread(cx, options, script->mSize)) {
             DecodeScriptOffThread(cx, script);
         } else {
             script->mReadyToExecute = true;
         }
     }
 
     LOG(Info, "Initialized decoding in %fms\n",
@@ -438,16 +459,17 @@ ScriptPreloader::PrepareCacheWrite()
 //
 // - A uint32 containing the size of the header block.
 //
 // - A header entry for each file stored in the cache containing:
 //   - The URL that the script was originally read from.
 //   - Its cache key.
 //   - The offset of its XDR data within the XDR data block.
 //   - The size of its XDR data in the XDR data block.
+//   - A bit field describing which process types the script is used in.
 //
 // - A block of XDR data for the encoded scripts, with each script's data at
 //   an offset from the start of the block, as specified above.
 Result<Ok, nsresult>
 ScriptPreloader::WriteCache()
 {
     MOZ_ASSERT(!NS_IsMainThread());
 
@@ -559,20 +581,23 @@ ScriptPreloader::NoteScript(const nsCStr
         restored = FindScript(mRestoredScripts, cachePath);
     }
 
     if (restored) {
         restored->remove();
         mSavedScripts.insertBack(restored);
 
         MOZ_ASSERT(script);
+        restored->mProcesses += CurrentProcessType();
         restored->mScript = script;
         restored->mReadyToExecute = true;
     } else if (!exists) {
         auto cachedScript = new CachedScript(url, cachePath, script);
+        cachedScript->mProcesses += CurrentProcessType();
+
         mSavedScripts.insertBack(cachedScript);
         mScripts.Put(cachePath, cachedScript);
     }
 }
 
 JSScript*
 ScriptPreloader::GetCachedScript(JSContext* cx, const nsCString& path)
 {
--- a/js/xpconnect/loader/ScriptPreloader.h
+++ b/js/xpconnect/loader/ScriptPreloader.h
@@ -1,16 +1,18 @@
 /* -*-  Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ScriptPreloader_h
 #define ScriptPreloader_h
 
+#include "mozilla/CheckedInt.h"
+#include "mozilla/EnumSet.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Range.h"
 #include "mozilla/Vector.h"
 #include "mozilla/Result.h"
 #include "mozilla/loader/AutoMemMap.h"
@@ -22,16 +24,22 @@
 
 #include "jsapi.h"
 
 #include <prio.h>
 
 namespace mozilla {
 namespace loader {
     class InputBuffer;
+
+    enum class ProcessType : uint8_t {
+        Parent,
+        Web,
+        Extension,
+    };
 }
 
 using namespace mozilla::loader;
 
 class ScriptPreloader : public nsIObserver
                       , public nsIMemoryReporter
                       , public nsIRunnable
 {
@@ -40,30 +48,37 @@ class ScriptPreloader : public nsIObserv
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIOBSERVER
     NS_DECL_NSIMEMORYREPORTER
     NS_DECL_NSIRUNNABLE
 
     static ScriptPreloader& GetSingleton();
 
+    static ProcessType GetChildProcessType(const nsAString& remoteType);
+
     // Retrieves the script with the given cache key from the script cache.
     // Returns null if the script is not cached.
     JSScript* GetCachedScript(JSContext* cx, const nsCString& name);
 
     // Notes the execution of a script with the given URL and cache key.
     // Depending on the stage of startup, the script may be serialized and
     // stored to the startup script cache.
     void NoteScript(const nsCString& url, const nsCString& cachePath, JS::HandleScript script);
 
     // Initializes the script cache from the startup script cache file.
     Result<Ok, nsresult> InitCache();
 
     void Trace(JSTracer* trc);
 
+    static ProcessType CurrentProcessType()
+    {
+        return sProcessType;
+    }
+
 protected:
     virtual ~ScriptPreloader() = default;
 
 private:
     // Represents a cached JS script, either initially read from the script
     // cache file, to be added to the next session's script cache file, or
     // both.
     //
@@ -121,16 +136,17 @@ private:
         // script cache file.
         template<typename Buffer>
         void Code(Buffer& buffer)
         {
             buffer.codeString(mURL);
             buffer.codeString(mCachePath);
             buffer.codeUint32(mOffset);
             buffer.codeUint32(mSize);
+            buffer.codeUint8(mProcessTypes);
         }
 
         // Returns the XDR data generated for this script during this session. See
         // mXDRData.
         JS::TranscodeBuffer& Data()
         {
             MOZ_ASSERT(mXDRData.isSome());
             return mXDRData.ref();
@@ -175,16 +191,19 @@ private:
         // is too small to be decoded off-thread, and may be immediately decoded
         // whenever it is first executed.
         bool mReadyToExecute = false;
 
         // The off-thread decode token for a completed off-thread decode, which
         // has not yet been finalized on the main thread.
         void* mToken = nullptr;
 
+        // The set of processes in which this script has been used.
+        EnumSet<ProcessType> mProcessTypes{};
+
         // The read-only XDR data for this script, which was either read from an
         // existing cache file, or generated by encoding a script which was
         // compiled during this session.
         Maybe<JS::TranscodeRange> mXDRRange;
 
         // XDR data which was generated from a script compiled during this
         // session, and will be written to the cache file.
         Maybe<JS::TranscodeBuffer> mXDRData;
@@ -272,16 +291,19 @@ private:
     // True after we've shown the first window, and are no longer adding new
     // scripts to the cache.
     bool mStartupFinished = false;
 
     bool mCacheInitialized = false;
     bool mSaveComplete = false;
     bool mDataPrepared = false;
 
+    // The process type of the current process.
+    static ProcessType sProcessType;
+
     nsCOMPtr<nsIFile> mProfD;
     nsCOMPtr<nsIThread> mSaveThread;
 
     // The mmapped cache data from this session's cache file.
     AutoMemMap mCacheData;
 
     Monitor mMonitor;
     Monitor mSaveMonitor;
--- a/js/xpconnect/loader/moz.build
+++ b/js/xpconnect/loader/moz.build
@@ -38,10 +38,12 @@ EXTRA_JS_MODULES += [
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../src',
     '../wrappers',
     '/dom/base',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-shadow']