Bug 1403348: Part 4 - Annotate certain startup crashes with AsyncShutdown script contents. r=mccr8 data-r=rweiss
☠☠ backed out by d4b3440ba8c9 ☠ ☠
authorKris Maglione <maglione.k@gmail.com>
Sat, 04 Nov 2017 17:20:17 -0700
changeset 462283 cf8613751378c8790b56131cd2a1be68573f9286
parent 462282 8e1184c068ad8b5ca561ba4cdb5a7ee90ac556e4
child 462284 9694cd0a527d30dc86067592da405ddef1203453
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1403348
milestone60.0a1
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 1403348: Part 4 - Annotate certain startup crashes with AsyncShutdown script contents. r=mccr8 data-r=rweiss We're seeing startup crashes which point to data corruption in the source of the AsyncShutdown component and module, but it's unclear whether the source of this corruption is on disk, in memory, or in XDR data. This change annotates crash reports with the contents of those files, so that we can compare the actual source with the corrupted values in the generated errors, and narrow down the source of the corruption. MozReview-Commit-ID: 7p8y73XUGLK
dom/serviceworkers/ServiceWorkerRegistrar.cpp
dom/workers/moz.build
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/loader/mozJSComponentLoader.h
--- a/dom/serviceworkers/ServiceWorkerRegistrar.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrar.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
+#include "mozJSComponentLoader.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsContentUtils.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
@@ -1129,16 +1130,17 @@ ServiceWorkerRegistrar::GetName(nsAStrin
 NS_IMETHODIMP
 ServiceWorkerRegistrar::GetState(nsIPropertyBag**)
 {
   return NS_OK;
 }
 
 #define RELEASE_ASSERT_SUCCEEDED(rv, name) do { \
     if (NS_FAILED(rv)) {                                                       \
+      mozJSComponentLoader::Get()->AnnotateCrashReport();                      \
       if (rv == NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS) {                  \
         if (auto* context = CycleCollectedJSContext::Get()) {                  \
           if (RefPtr<Exception> exn = context->GetPendingException()) {        \
             MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s",              \
                                     exn->GetMessageMoz().get());               \
           }                                                                    \
         }                                                                      \
       }                                                                        \
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -64,16 +64,17 @@ UNIFIED_SOURCES += [
     'WorkerThread.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
     '/dom/base',
     '/dom/bindings',
+    '/js/xpconnect/loader',
     '/xpcom/build',
     '/xpcom/threads',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -44,23 +44,28 @@
 #include "AutoMemMap.h"
 #include "ScriptPreloader-inl.h"
 
 #include "mozilla/AddonPathService.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "mozilla/MacroForEach.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/ResultExtensions.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Unused.h"
 
+#ifdef MOZ_CRASHREPORTER
+#include "mozilla/ipc/CrashReporterClient.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::scache;
 using namespace mozilla::loader;
 using namespace xpc;
 using namespace JS;
 
 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
 
@@ -354,16 +359,55 @@ ResolveModuleObjectProperty(JSContext* a
         }
         if (found) {
             return lexical;
         }
     }
     return aModObj;
 }
 
+#ifdef MOZ_CRASHREPORTER
+static mozilla::Result<nsCString, nsresult> ReadScript(ComponentLoaderInfo& aInfo);
+
+static nsresult
+AnnotateScriptContents(const nsACString& aName, const nsACString& aURI)
+{
+    ComponentLoaderInfo info(aURI);
+
+    nsCString str;
+    MOZ_TRY_VAR(str, ReadScript(info));
+
+    // The crash reporter won't accept any strings with embedded nuls. We
+    // shouldn't have any here, but if we do because of data corruption, we
+    // still want the annotation. So replace any embedded nuls before
+    // annotating.
+    str.ReplaceSubstring(NS_LITERAL_CSTRING("\0"), NS_LITERAL_CSTRING("\\0"));
+
+    CrashReporter::AnnotateCrashReport(aName, str);
+
+    return NS_OK;
+}
+#endif // defined MOZ_CRASHREPORTER
+
+nsresult
+mozJSComponentLoader::AnnotateCrashReport()
+{
+#ifdef MOZ_CRASHREPORTER
+    Unused << AnnotateScriptContents(
+        NS_LITERAL_CSTRING("nsAsyncShutdownComponent"),
+        NS_LITERAL_CSTRING("resource://gre/components/nsAsyncShutdown.js"));
+
+    Unused << AnnotateScriptContents(
+        NS_LITERAL_CSTRING("AsyncShutdownModule"),
+        NS_LITERAL_CSTRING("resource://gre/modules/AsyncShutdown.jsm"));
+#endif // defined MOZ_CRASHREPORTER
+
+    return NS_OK;
+}
+
 const mozilla::Module*
 mozJSComponentLoader::LoadModule(FileLocation& aFile)
 {
     if (!NS_IsMainThread()) {
         MOZ_ASSERT(false, "Don't use JS components off the main thread");
         return nullptr;
     }
 
@@ -396,16 +440,18 @@ mozJSComponentLoader::LoadModule(FileLoc
 
     nsAutoPtr<ModuleEntry> entry(new ModuleEntry(RootingContext::get(cx)));
     RootedValue exn(cx);
     rv = ObjectForLocation(info, file, &entry->obj, &entry->thisObjectKey,
                            &entry->location, isCriticalModule, &exn);
     if (NS_FAILED(rv)) {
         // Temporary debugging assertion for bug 1403348:
         if (isCriticalModule && !exn.isUndefined()) {
+            AnnotateCrashReport();
+
             JSAutoCompartment ac(cx, xpc::PrivilegedJunkScope());
             JS_WrapValue(cx, &exn);
 
             nsAutoCString file;
             uint32_t line;
             uint32_t column;
             nsAutoString msg;
             nsContentUtils::ExtractErrorValues(cx, exn, file, &line, &column, msg);
@@ -700,16 +746,46 @@ mozJSComponentLoader::PrepareObjectForLo
         dom::AutoEntryScript aes(globalObj,
                                  "component loader report global");
         JS_FireOnNewGlobalObject(aes.cx(), globalObj);
     }
 
     return thisObj;
 }
 
+static mozilla::Result<nsCString, nsresult>
+ReadScript(ComponentLoaderInfo& aInfo)
+{
+    MOZ_TRY(aInfo.EnsureScriptChannel());
+
+    nsCOMPtr<nsIInputStream> scriptStream;
+    MOZ_TRY(NS_MaybeOpenChannelUsingOpen2(aInfo.ScriptChannel(),
+                                          getter_AddRefs(scriptStream)));
+
+    uint64_t len64;
+    uint32_t bytesRead;
+
+    MOZ_TRY(scriptStream->Available(&len64));
+    NS_ENSURE_TRUE(len64 < UINT32_MAX, Err(NS_ERROR_FILE_TOO_BIG));
+    NS_ENSURE_TRUE(len64, Err(NS_ERROR_FAILURE));
+    uint32_t len = (uint32_t)len64;
+
+    /* malloc an internal buf the size of the file */
+    nsCString str;
+    if (!str.SetLength(len, fallible))
+        return Err(NS_ERROR_OUT_OF_MEMORY);
+
+    /* read the file in one swoop */
+    MOZ_TRY(scriptStream->Read(str.BeginWriting(), len, &bytesRead));
+    if (bytesRead != len)
+        return Err(NS_BASE_STREAM_OSERROR);
+
+    return Move(str);
+}
+
 nsresult
 mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
                                         nsIFile* aComponentFile,
                                         MutableHandleObject aObject,
                                         MutableHandleScript aTableScript,
                                         char** aLocation,
                                         bool aPropagateExceptions,
                                         MutableHandleValue aException)
@@ -790,49 +866,23 @@ mozJSComponentLoader::ObjectForLocation(
             // Note: exceptions will get handled further down;
             // don't early return for them here.
             auto buf = map.get<char>();
             if (reuseGlobal)
                 CompileForNonSyntacticScope(cx, options, buf.get(), map.size(), &script);
             else
                 Compile(cx, options, buf.get(), map.size(), &script);
         } else {
-            rv = aInfo.EnsureScriptChannel();
-            NS_ENSURE_SUCCESS(rv, rv);
-            nsCOMPtr<nsIInputStream> scriptStream;
-            rv = NS_MaybeOpenChannelUsingOpen2(aInfo.ScriptChannel(),
-                   getter_AddRefs(scriptStream));
-            NS_ENSURE_SUCCESS(rv, rv);
-
-            uint64_t len64;
-            uint32_t bytesRead;
-
-            rv = scriptStream->Available(&len64);
-            NS_ENSURE_SUCCESS(rv, rv);
-            NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-            if (!len64)
-                return NS_ERROR_FAILURE;
-            uint32_t len = (uint32_t)len64;
-
-            /* malloc an internal buf the size of the file */
-            auto buf = MakeUniqueFallible<char[]>(len + 1);
-            if (!buf)
-                return NS_ERROR_OUT_OF_MEMORY;
-
-            /* read the file in one swoop */
-            rv = scriptStream->Read(buf.get(), len, &bytesRead);
-            if (bytesRead != len)
-                return NS_BASE_STREAM_OSERROR;
-
-            buf[len] = '\0';
+            nsCString str;
+            MOZ_TRY_VAR(str, ReadScript(aInfo));
 
             if (reuseGlobal)
-                CompileForNonSyntacticScope(cx, options, buf.get(), bytesRead, &script);
+                CompileForNonSyntacticScope(cx, options, str.get(), str.Length(), &script);
             else
-                Compile(cx, options, buf.get(), bytesRead, &script);
+                Compile(cx, options, str.get(), str.Length(), &script);
         }
         // Propagate the exception, if one exists. Also, don't leave the stale
         // exception on this context.
         if (!script && aPropagateExceptions && jsapi.HasException()) {
             if (!jsapi.StealException(aException))
                 return NS_ERROR_OUT_OF_MEMORY;
         }
     }
--- a/js/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/xpconnect/loader/mozJSComponentLoader.h
@@ -73,16 +73,24 @@ class mozJSComponentLoader final : publi
     nsresult Unload(const nsACString& aResourceURI);
     nsresult IsModuleLoaded(const nsACString& aResourceURI, bool* aRetval);
     bool IsLoaderGlobal(JSObject* aObj) {
         return mLoaderGlobal == aObj;
     }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
+    /**
+     * Temporary diagnostic function for startup crashes in bug 1403348:
+     *
+     * Annotate the crash report with the contents of the async shutdown
+     * module/component scripts.
+     */
+    nsresult AnnotateCrashReport();
+
  protected:
     virtual ~mozJSComponentLoader();
 
     friend class mozilla::ScriptPreloader;
 
     JSObject* CompilationScope(JSContext* aCx)
     {
         if (mLoaderGlobal)