Bug 1403348: Part 4 - Annotate certain startup crashes with AsyncShutdown script contents. r?mccr8 data-r?rweiss draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 04 Nov 2017 17:20:17 -0700
changeset 693282 5b9392666ec8918ca1e205257c37922c509a301f
parent 693266 79b244b957322e2923f6357ce5dc83c8c21e9e65
child 738981 50f07fc636cdda07df95d240161f4ccf40de208c
push id87742
push usermaglione.k@gmail.com
push dateSun, 05 Nov 2017 00:25:24 +0000
reviewersmccr8
bugs1403348
milestone58.0a1
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/workers/ServiceWorkerRegistrar.cpp
dom/workers/moz.build
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/loader/mozJSComponentLoader.h
--- a/dom/workers/ServiceWorkerRegistrar.cpp
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -19,16 +19,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"
@@ -1115,16 +1116,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 (nsCOMPtr<nsIException> exn = context->GetPendingException()) {   \
             nsAutoCString msg;                                                 \
             if (NS_SUCCEEDED(exn->GetMessageMoz(msg))) {                       \
               MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s", msg.get());\
             }                                                                  \
           }                                                                    \
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -102,16 +102,17 @@ IPDL_SOURCES += [
     'ServiceWorkerRegistrarTypes.ipdlh',
 ]
 
 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,21 +44,26 @@
 #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/ScriptSettings.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";
 
@@ -351,16 +356,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;
     }
 
@@ -393,16 +437,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);
@@ -699,16 +745,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
@@ -63,16 +63,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)