Bug 1194856 - Add crash report annotations just before crashing in ErrorLoadingBuiltinSheet. r=bsmedberg a=sledru
authorCameron McCormack <cam@mcc.id.au>
Thu, 15 Oct 2015 19:00:47 +1100
changeset 289540 b35d2786d527
parent 289539 435d2901f5b5
child 289541 655607703bb1
push id5176
push usercmccormack@mozilla.com
push date2015-10-15 08:00 +0000
treeherdermozilla-beta@b35d2786d527 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, sledru
bugs1194856
milestone42.0
Bug 1194856 - Add crash report annotations just before crashing in ErrorLoadingBuiltinSheet. r=bsmedberg a=sledru
layout/style/nsLayoutStylesheetCache.cpp
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -13,16 +13,27 @@
 #include "mozilla/css/Loader.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsPrintfCString.h"
 
+// Includes for the crash report annotation in ErrorLoadingBuiltinSheet.
+#ifdef MOZ_CRASHREPORTER
+#include "mozilla/Omnijar.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsExceptionHandler.h"
+#include "nsIChromeRegistry.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISubstitutingProtocolHandler.h"
+#endif
+
 using namespace mozilla;
 
 static bool sNumberControlEnabled;
 
 #define NUMBER_CONTROL_PREF "dom.forms.number"
 
 NS_IMPL_ISUPPORTS(
   nsLayoutStylesheetCache, nsIObserver, nsIMemoryReporter)
@@ -459,19 +470,259 @@ nsLayoutStylesheetCache::LoadSheetFile(n
   if (!exists) return;
 
   nsCOMPtr<nsIURI> uri;
   NS_NewFileURI(getter_AddRefs(uri), aFile);
 
   LoadSheet(uri, aSheet, false);
 }
 
+#ifdef MOZ_CRASHREPORTER
+static void
+ListInterestingFiles(nsString& aAnnotation, nsIFile* aFile,
+                     const nsTArray<nsString>& aInterestingFilenames)
+{
+  nsString filename;
+  aFile->GetLeafName(filename);
+  for (const nsString& interestingFilename : aInterestingFilenames) {
+    if (interestingFilename == filename) {
+      nsString path;
+      aFile->GetPath(path);
+      aAnnotation.AppendLiteral("  ");
+      aAnnotation.Append(path);
+      aAnnotation.AppendLiteral(" (");
+      int64_t size;
+      if (NS_SUCCEEDED(aFile->GetFileSize(&size))) {
+        aAnnotation.AppendPrintf("%ld", size);
+      } else {
+        aAnnotation.AppendLiteral("???");
+      }
+      aAnnotation.AppendLiteral(" bytes)\n");
+      return;
+    }
+  }
+
+  bool isDir = false;
+  aFile->IsDirectory(&isDir);
+
+  if (!isDir) {
+    return;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  if (NS_FAILED(aFile->GetDirectoryEntries(getter_AddRefs(entries)))) {
+    aAnnotation.AppendLiteral("  (failed to enumerated directory)\n");
+    return;
+  }
+
+  for (;;) {
+    bool hasMore = false;
+    if (NS_FAILED(entries->HasMoreElements(&hasMore))) {
+      aAnnotation.AppendLiteral("  (failed during directory enumeration)\n");
+      return;
+    }
+    if (!hasMore) {
+      break;
+    }
+
+    nsCOMPtr<nsISupports> entry;
+    if (NS_FAILED(entries->GetNext(getter_AddRefs(entry)))) {
+      aAnnotation.AppendLiteral("  (failed during directory enumeration)\n");
+      return;
+    }
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    if (file) {
+      ListInterestingFiles(aAnnotation, file, aInterestingFilenames);
+    }
+  }
+}
+
+// Generate a crash report annotation to help debug issues with style
+// sheets failing to load (bug 1194856).
+static void
+AnnotateCrashReport(nsIURI* aURI)
+{
+  nsAutoCString spec;
+  nsAutoCString scheme;
+  nsDependentCSubstring filename;
+  if (aURI) {
+    aURI->GetSpec(spec);
+    aURI->GetScheme(scheme);
+    int32_t i = spec.RFindChar('/');
+    if (i != -1) {
+      filename.Rebind(spec, i + 1);
+    }
+  }
+
+  nsString annotation;
+
+  // The URL of the sheet that failed to load.
+  annotation.AppendLiteral("Error loading sheet: ");
+  annotation.Append(NS_ConvertUTF8toUTF16(spec).get());
+  annotation.Append('\n');
+
+  // The jar: or file: URL that the sheet's resource: or chrome: URL
+  // resolves to.
+  if (scheme.EqualsLiteral("resource")) {
+    annotation.AppendLiteral("Real location: ");
+    nsCOMPtr<nsISubstitutingProtocolHandler> handler;
+    nsCOMPtr<nsIIOService> io(do_GetIOService());
+    if (io) {
+      nsCOMPtr<nsIProtocolHandler> ph;
+      io->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
+      if (ph) {
+        handler = do_QueryInterface(ph);
+      }
+    }
+    if (!handler) {
+      annotation.AppendLiteral("(ResolveURI failed)\n");
+    } else {
+      nsAutoCString resolvedSpec;
+      handler->ResolveURI(aURI, resolvedSpec);
+      annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec));
+      annotation.Append('\n');
+    }
+  } else if (scheme.EqualsLiteral("chrome")) {
+    annotation.AppendLiteral("Real location: ");
+    nsCOMPtr<nsIChromeRegistry> reg =
+      mozilla::services::GetChromeRegistryService();
+    if (!reg) {
+      annotation.AppendLiteral("(no chrome registry)\n");
+    } else {
+      nsCOMPtr<nsIURI> resolvedURI;
+      reg->ConvertChromeURL(aURI, getter_AddRefs(resolvedURI));
+      if (!resolvedURI) {
+        annotation.AppendLiteral("(ConvertChromeURL failed)\n");
+      } else {
+        nsAutoCString resolvedSpec;
+        resolvedURI->GetSpec(resolvedSpec);
+        annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec));
+        annotation.Append('\n');
+      }
+    }
+  }
+
+  nsTArray<nsString> interestingFiles;
+  interestingFiles.AppendElement(NS_LITERAL_STRING("chrome.manifest"));
+  interestingFiles.AppendElement(NS_LITERAL_STRING("omni.ja"));
+  interestingFiles.AppendElement(NS_ConvertUTF8toUTF16(filename));
+
+  annotation.AppendLiteral("GRE directory: ");
+  nsCOMPtr<nsIFile> file;
+  nsDirectoryService::gService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
+                                    getter_AddRefs(file));
+  if (file) {
+    // The Firefox installation directory.
+    nsString path;
+    file->GetPath(path);
+    annotation.Append(path);
+    annotation.Append('\n');
+
+    // List interesting files -- any chrome.manifest or omni.ja file or any file
+    // whose name is the sheet's filename -- under the Firefox installation
+    // directory.
+    annotation.AppendLiteral("Interesting files in the GRE directory:\n");
+    ListInterestingFiles(annotation, file, interestingFiles);
+
+    // If the Firefox installation directory has a chrome.manifest file, let's
+    // see what's in it.
+    file->Append(NS_LITERAL_STRING("chrome.manifest"));
+    bool exists = false;
+    file->Exists(&exists);
+    if (exists) {
+      annotation.AppendLiteral("Contents of chrome.manifest:\n[[[\n");
+      PRFileDesc* fd;
+      if (NS_SUCCEEDED(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
+        nsCString contents;
+        char buf[512];
+        int32_t n;
+        while ((n = PR_Read(fd, buf, sizeof(buf))) > 0) {
+          contents.Append(buf, n);
+        }
+        if (n < 0) {
+          annotation.AppendLiteral("  (error while reading)\n");
+        } else {
+          annotation.Append(NS_ConvertUTF8toUTF16(contents));
+        }
+        PR_Close(fd);
+      }
+      annotation.AppendLiteral("]]]\n");
+    }
+  } else {
+    annotation.AppendLiteral("(none)\n");
+  }
+
+  // The jar: or file: URL prefix that chrome: and resource: URLs get translated
+  // to.
+  annotation.AppendLiteral("GRE omnijar URI string: ");
+  nsCString uri;
+  nsresult rv = Omnijar::GetURIString(Omnijar::GRE, uri);
+  if (NS_FAILED(rv)) {
+    annotation.AppendLiteral("(failed)\n");
+  } else {
+    annotation.Append(NS_ConvertUTF8toUTF16(uri));
+    annotation.Append('\n');
+  }
+
+  nsRefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::GRE);
+  if (zip) {
+    // List interesting files in the GRE omnijar.
+    annotation.AppendLiteral("Interesting files in the GRE omnijar:\n");
+    nsZipFind* find;
+    rv = zip->FindInit(nullptr, &find);
+    if (NS_FAILED(rv)) {
+      annotation.AppendPrintf("  (FindInit failed with 0x%08x)\n", rv);
+    } else if (!find) {
+      annotation.AppendLiteral("  (FindInit returned null)\n");
+    } else {
+      const char* result;
+      uint16_t len;
+      while (NS_SUCCEEDED(find->FindNext(&result, &len))) {
+        nsCString itemPathname;
+        nsString itemFilename;
+        itemPathname.Append(result, len);
+        int32_t i = itemPathname.RFindChar('/');
+        if (i != -1) {
+          itemFilename = NS_ConvertUTF8toUTF16(Substring(itemPathname, i + 1));
+        }
+        for (const nsString& interestingFile : interestingFiles) {
+          if (interestingFile == itemFilename) {
+            annotation.AppendLiteral("  ");
+            annotation.Append(NS_ConvertUTF8toUTF16(itemPathname));
+            nsZipItem* item = zip->GetItem(itemPathname.get());
+            if (!item) {
+              annotation.AppendLiteral(" (GetItem failed)\n");
+            } else {
+              annotation.AppendPrintf(" (%d bytes, crc32 = 0x%08x)\n",
+                                      item->RealSize(),
+                                      item->CRC32());
+            }
+            break;
+          }
+        }
+      }
+      delete find;
+    }
+  } else {
+    annotation.AppendLiteral("No GRE omnijar\n");
+  }
+
+  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SheetLoadFailure"),
+                                     NS_ConvertUTF16toUTF8(annotation));
+}
+#endif
+
 static void
 ErrorLoadingBuiltinSheet(nsIURI* aURI, const char* aMsg)
 {
+#ifdef MOZ_CRASHREPORTER
+  AnnotateCrashReport(aURI);
+#endif
+
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
   NS_RUNTIMEABORT(nsPrintfCString("%s loading built-in stylesheet '%s'",
                                   aMsg, spec.get()).get());
 }