Bug 1310142 - Preserve backup databases and raw table updates for diagnostic. r=francois draft
authorHenry Chang <hchang@mozilla.com>
Fri, 14 Oct 2016 17:58:18 +0800
changeset 433823 02585bca9853a00bc041874a999b39e6fd7246ea
parent 433792 8002299dfc3fb3b2c53e50e40a8e16f8dff2c3e7
child 535971 47f05b0f1a4f50751e6cd9f2e4f71b6f1a1ac84e
push id34666
push userhchang@mozilla.com
push dateFri, 04 Nov 2016 10:45:30 +0000
reviewersfrancois
bugs1310142
milestone52.0a1
Bug 1310142 - Preserve backup databases and raw table updates for diagnostic. r=francois MozReview-Commit-ID: GeJoBrhuTgA
toolkit/components/url-classifier/Classifier.cpp
toolkit/components/url-classifier/Classifier.h
toolkit/components/url-classifier/ProtocolParser.h
toolkit/components/url-classifier/moz.build
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/nsUrlClassifierDBService.h
--- a/toolkit/components/url-classifier/Classifier.cpp
+++ b/toolkit/components/url-classifier/Classifier.cpp
@@ -584,16 +584,19 @@ Classifier::ApplyUpdates(nsTArray<TableU
         if (TableUpdate::Cast<TableUpdateV2>((*aUpdates)[i])) {
           rv = UpdateHashStore(aUpdates, updateTable);
         } else {
           rv = UpdateTableV4(aUpdates, updateTable);
         }
 
         if (NS_FAILED(rv)) {
           if (rv != NS_ERROR_OUT_OF_MEMORY) {
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+            DumpFailedUpdate();
+#endif
             AbortUpdateAndReset(updateTable);
           }
           return rv;
         }
       }
     }
 
   } // End of scopedUpdatesClearer scope.
@@ -753,16 +756,94 @@ Classifier::CleanToDelete()
   if (exists) {
     rv = mToDeleteDirectory->Remove(true);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+
+already_AddRefed<nsIFile>
+Classifier::GetFailedUpdateDirectroy()
+{
+  nsCString failedUpdatekDirName = STORE_DIRECTORY + nsCString("-failedupdate");
+
+  nsCOMPtr<nsIFile> failedUpdatekDirectory;
+  if (NS_FAILED(mCacheDirectory->Clone(getter_AddRefs(failedUpdatekDirectory))) ||
+      NS_FAILED(failedUpdatekDirectory->AppendNative(failedUpdatekDirName))) {
+    LOG(("Failed to init failedUpdatekDirectory."));
+    return nullptr;
+  }
+
+  return failedUpdatekDirectory.forget();
+}
+
+nsresult
+Classifier::DumpRawTableUpdates(const nsACString& aRawUpdates)
+{
+  // DumpFailedUpdate() MUST be called first to create the
+  // "failed update" directory
+
+  LOG(("Dumping raw table updates..."));
+
+  nsCOMPtr<nsIFile> failedUpdatekDirectory = GetFailedUpdateDirectroy();
+
+  // Create tableupdate.bin and dump raw table update data.
+  nsCOMPtr<nsIFile> rawTableUpdatesFile;
+  nsCOMPtr<nsIOutputStream> outputStream;
+  if (NS_FAILED(failedUpdatekDirectory->Clone(getter_AddRefs(rawTableUpdatesFile))) ||
+      NS_FAILED(rawTableUpdatesFile->AppendNative(nsCString("tableupdates.bin"))) ||
+      NS_FAILED(NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
+                                            rawTableUpdatesFile,
+                                            PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE))) {
+    LOG(("Failed to create file to dump raw table updates."));
+    return NS_ERROR_FAILURE;
+  }
+
+  // Write out the data.
+  uint32_t written;
+  nsresult rv = outputStream->Write(aRawUpdates.BeginReading(),
+                                    aRawUpdates.Length(), &written);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(written == aRawUpdates.Length(), NS_ERROR_FAILURE);
+
+  return rv;
+}
+
+nsresult
+Classifier::DumpFailedUpdate()
+{
+  LOG(("Dumping failed update..."));
+
+  nsCOMPtr<nsIFile> failedUpdatekDirectory = GetFailedUpdateDirectroy();
+
+  // Remove the "failed update" directory no matter it exists or not.
+  // Failure is fine because the directory may not exist.
+  failedUpdatekDirectory->Remove(true);
+
+  nsCString failedUpdatekDirName;
+  nsresult rv = failedUpdatekDirectory->GetNativeLeafName(failedUpdatekDirName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Move backup to a clean "failed update" directory.
+  nsCOMPtr<nsIFile> backupDirectory;
+  if (NS_FAILED(mBackupDirectory->Clone(getter_AddRefs(backupDirectory))) ||
+      NS_FAILED(backupDirectory->MoveToNative(nullptr, failedUpdatekDirName))) {
+    LOG(("Failed to move backup to the \"failed update\" directory %s",
+         failedUpdatekDirName.get()));
+    return NS_ERROR_FAILURE;
+  }
+
+  return rv;
+}
+
+#endif // MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+
 nsresult
 Classifier::BackupTables()
 {
   // We have to work in reverse here: first move the normal directory
   // away to be the backup directory, then copy the files over
   // to the normal directory. This ensures that if we crash the backup
   // dir always has a valid, complete copy, instead of a partial one,
   // because that's the one we will copy over the normal store dir.
--- a/toolkit/components/url-classifier/Classifier.h
+++ b/toolkit/components/url-classifier/Classifier.h
@@ -79,16 +79,21 @@ public:
   /*
    * Get a bunch of extra prefixes to query for completion
    * and mask the real entry being requested
    */
   nsresult ReadNoiseEntries(const Prefix& aPrefix,
                             const nsACString& aTableName,
                             uint32_t aCount,
                             PrefixArray* aNoiseEntries);
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+  nsresult DumpRawTableUpdates(const nsACString& aRawUpdates);
+#endif
+
   static void SplitTables(const nsACString& str, nsTArray<nsCString>& tables);
 
   // Given a root store directory, return a private store directory
   // based on the table name. To avoid migration issue, the private
   // store directory is only different from root directory for V4 tables.
   //
   // For V4 tables (suffixed by '-proto'), the private directory would
   // be [root directory path]/[provider]. The provider of V4 tables is
@@ -107,16 +112,22 @@ private:
 
   nsresult CreateStoreDirectory();
   nsresult SetupPathNames();
   nsresult RecoverBackups();
   nsresult CleanToDelete();
   nsresult BackupTables();
   nsresult RemoveBackupTables();
   nsresult RegenActiveTables();
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+  already_AddRefed<nsIFile> GetFailedUpdateDirectroy();
+  nsresult DumpFailedUpdate();
+#endif
+
   nsresult ScanStoreDir(nsTArray<nsCString>& aTables);
 
   nsresult UpdateHashStore(nsTArray<TableUpdate*>* aUpdates,
                            const nsACString& aTable);
 
   nsresult UpdateTableV4(nsTArray<TableUpdate*>* aUpdates,
                          const nsACString& aTable);
 
--- a/toolkit/components/url-classifier/ProtocolParser.h
+++ b/toolkit/components/url-classifier/ProtocolParser.h
@@ -25,16 +25,20 @@ public:
 
   ProtocolParser();
   virtual ~ProtocolParser();
 
   nsresult Status() const { return mUpdateStatus; }
 
   nsresult Init(nsICryptoHash* aHasher);
 
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+  nsCString GetRawTableUpdates() const { return mPending; }
+#endif
+
   virtual void SetCurrentTable(const nsACString& aTable) = 0;
 
   void SetRequestedTables(const nsTArray<nsCString>& aRequestTables)
   {
     mRequestedTables = aRequestTables;
   }
 
   nsresult Begin();
--- a/toolkit/components/url-classifier/moz.build
+++ b/toolkit/components/url-classifier/moz.build
@@ -76,8 +76,11 @@ LOCAL_INCLUDES += [
     '../build',
     '/ipc/chromium/src',
 ]
 
 CXXFLAGS += CONFIG['SQLITE_CFLAGS']
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['NIGHTLY_BUILD'] or CONFIG['MOZ_DEBUG']:
+    DEFINES['MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES'] = True
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -552,16 +552,21 @@ nsUrlClassifierDBServiceWorker::FinishSt
     for (uint32_t i = 0; i < forwards.Length(); i++) {
       const ProtocolParser::ForwardedUpdate &forward = forwards[i];
       mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
     }
     // Hold on to any TableUpdate objects that were created by the
     // parser.
     mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates());
     mProtocolParser->ForgetTableUpdates();
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+    // The assignment involves no string copy since the source string is sharable.
+    mRawTableUpdates = mProtocolParser->GetRawTableUpdates();
+#endif
   } else {
     LOG(("nsUrlClassifierDBService::FinishStream Failed to parse the stream "
          "using mProtocolParser."));
     mUpdateStatus = mProtocolParser->Status();
   }
   mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
 
   if (NS_SUCCEEDED(mUpdateStatus)) {
@@ -617,17 +622,27 @@ nsUrlClassifierDBServiceWorker::FinishUp
 
   return NS_OK;
 }
 
 nsresult
 nsUrlClassifierDBServiceWorker::ApplyUpdate()
 {
   LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate()"));
-  return mClassifier->ApplyUpdates(&mTableUpdates);
+  nsresult rv = mClassifier->ApplyUpdates(&mTableUpdates);
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+  if (NS_FAILED(rv) && NS_ERROR_OUT_OF_MEMORY != rv) {
+    mClassifier->DumpRawTableUpdates(mRawTableUpdates);
+  }
+  // Invalidate the raw table updates.
+  mRawTableUpdates = EmptyCString();
+#endif
+
+  return rv;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::ResetDatabase()
 {
   nsresult rv = OpenDb();
 
   if (NS_SUCCEEDED(rv)) {
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -244,13 +244,18 @@ private:
     mozilla::TimeStamp mStartTime;
     nsCString mKey;
     nsCString mTables;
     nsCOMPtr<nsIUrlClassifierLookupCallback> mCallback;
   };
 
   // list of pending lookups
   nsTArray<PendingLookup> mPendingLookups;
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+  // The raw update response for debugging.
+  nsCString mRawTableUpdates;
+#endif
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
 
 #endif // nsUrlClassifierDBService_h_