Bug 987248 - Prevent divide-by-zero in seer. r=mcmanus
authorNicholas Hurley <hurley@todesschaf.org>
Mon, 07 Apr 2014 12:45:45 -0700
changeset 195836 f6116de9c5f32c2b4c2b982987b6cf145fd7dd4c
parent 195835 423df46d8d570b55348c735fc2d0fc446c44807c
child 195837 e43020f1f49118a99e8f381ead8cb791f421f88d
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs987248
milestone31.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 987248 - Prevent divide-by-zero in seer. r=mcmanus
netwerk/base/src/Seer.cpp
toolkit/components/telemetry/Histograms.json
--- a/netwerk/base/src/Seer.cpp
+++ b/netwerk/base/src/Seer.cpp
@@ -120,16 +120,20 @@ struct SeerTelemetryAccumulators {
   Telemetry::AutoCounter<Telemetry::SEER_PREDICT_ATTEMPTS> mPredictAttempts;
   Telemetry::AutoCounter<Telemetry::SEER_LEARN_ATTEMPTS> mLearnAttempts;
   Telemetry::AutoCounter<Telemetry::SEER_PREDICT_FULL_QUEUE> mPredictFullQueue;
   Telemetry::AutoCounter<Telemetry::SEER_LEARN_FULL_QUEUE> mLearnFullQueue;
   Telemetry::AutoCounter<Telemetry::SEER_TOTAL_PREDICTIONS> mTotalPredictions;
   Telemetry::AutoCounter<Telemetry::SEER_TOTAL_PRECONNECTS> mTotalPreconnects;
   Telemetry::AutoCounter<Telemetry::SEER_TOTAL_PRERESOLVES> mTotalPreresolves;
   Telemetry::AutoCounter<Telemetry::SEER_PREDICTIONS_CALCULATED> mPredictionsCalculated;
+  Telemetry::AutoCounter<Telemetry::SEER_LOAD_COUNT_IS_ZERO> mLoadCountZeroes;
+  Telemetry::AutoCounter<Telemetry::SEER_LOAD_COUNT_OVERFLOWS> mLoadCountOverflows;
+  Telemetry::AutoCounter<Telemetry::SEER_STARTUP_COUNT_IS_ZERO> mStartupCountZeroes;
+  Telemetry::AutoCounter<Telemetry::SEER_STARTUP_COUNT_OVERFLOWS> mStartupCountOverflows;
 };
 
 // Listener for the speculative DNS requests we'll fire off, which just ignores
 // the result (since we're just trying to warm the cache). This also exists to
 // reduce round-trips to the main thread, by being something threadsafe the Seer
 // can use.
 
 class SeerDNSListener : public nsIDNSListener
@@ -649,16 +653,23 @@ Seer::EnsureInitStorage()
     stmt = nullptr;
 
     rv = mDB->CreateStatement(
         NS_LITERAL_CSTRING("UPDATE moz_startups SET startups = :startup_count, "
                            "last_startup = :startup_time;\n"),
         getter_AddRefs(stmt));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    int32_t newStartupCount = mStartupCount + 1;
+    if (newStartupCount <= 0) {
+      SEER_LOG(("Seer::EnsureInitStorage startup count overflow\n"));
+      newStartupCount = mStartupCount;
+      ++mAccumulators->mStartupCountOverflows;
+    }
+
     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("startup_count"),
                                mStartupCount + 1);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"),
                                mStartupTime);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1291,20 +1302,28 @@ Seer::UpdateTopLevel(QueryType queryType
         NS_LITERAL_CSTRING("UPDATE moz_hosts SET loads = :load_count, "
                            "last_load = :now WHERE id = :id;"));
   }
   if (!stmt) {
     return;
   }
   mozStorageStatementScoper scope(stmt);
 
+  int32_t newLoadCount = info.loadCount + 1;
+  if (newLoadCount <= 0) {
+    SEER_LOG(("Seer::UpdateTopLevel type %d id %d load count overflow\n",
+              queryType, info.id));
+    newLoadCount = info.loadCount;
+    ++mAccumulators->mLoadCountOverflows;
+  }
+
   // First, let's update the page in the database, since loading a page
   // implicitly learns about the page.
   nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("load_count"),
-                                      info.loadCount + 1);
+                                      newLoadCount);
   RETURN_IF_FAILED(rv);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now);
   RETURN_IF_FAILED(rv);
 
   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id);
   RETURN_IF_FAILED(rv);
 
@@ -1317,16 +1336,22 @@ Seer::UpdateTopLevel(QueryType queryType
 // @param queryType - whether to predict based on page or origin
 // @param info - the db info about the top-level resource
 bool
 Seer::TryPredict(QueryType queryType, const TopLevelInfo &info, PRTime now,
                  SeerVerifierHandle &verifier, TimeStamp &predictStartTime)
 {
   MOZ_ASSERT(!NS_IsMainThread(), "TryPredict called on main thread.");
 
+  if (!info.loadCount) {
+    SEER_LOG(("Seer::TryPredict info.loadCount is zero!\n"));
+    ++mAccumulators->mLoadCountZeroes;
+    return false;
+  }
+
   int globalDegradation = CalculateGlobalDegradation(now, info.lastLoad);
 
   // Now let's look up the subresources we know about for this page
   nsCOMPtr<mozIStorageStatement> stmt;
   if (queryType == QUERY_PAGE) {
     stmt = mStatements.GetCachedStatement(
         NS_LITERAL_CSTRING("SELECT uri, hits, last_hit FROM moz_subresources "
                            "WHERE pid = :id;"));
@@ -1397,16 +1422,22 @@ nextrow:
 }
 
 // Find out if a top-level page is likely to redirect.
 bool
 Seer::WouldRedirect(const TopLevelInfo &info, PRTime now, UriInfo &newUri)
 {
   MOZ_ASSERT(!NS_IsMainThread(), "WouldRedirect called on main thread.");
 
+  if (!info.loadCount) {
+    SEER_LOG(("Seer::WouldRedirect info.loadCount is zero!\n"));
+    ++mAccumulators->mLoadCountZeroes;
+    return false;
+  }
+
   nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
       NS_LITERAL_CSTRING("SELECT uri, origin, hits, last_hit "
                          "FROM moz_redirects WHERE pid = :id;"));
   NS_ENSURE_TRUE(stmt, false);
   mozStorageStatementScoper scope(stmt);
 
   nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id);
   NS_ENSURE_SUCCESS(rv, false);
@@ -1527,16 +1558,22 @@ Seer::PredictForPageload(const UriInfo &
 // This is the driver for predicting at browser startup time based on pages that
 // have previously been loaded close to startup.
 void
 Seer::PredictForStartup(SeerVerifierHandle &verifier,
                         TimeStamp &predictStartTime)
 {
   MOZ_ASSERT(!NS_IsMainThread(), "PredictForStartup called on main thread");
 
+  if (!mStartupCount) {
+    SEER_LOG(("Seer::PredictForStartup mStartupCount is zero!\n"));
+    ++mAccumulators->mStartupCountZeroes;
+    return;
+  }
+
   if (NS_FAILED(EnsureInitStorage())) {
     return;
   }
 
   nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
       NS_LITERAL_CSTRING("SELECT uri, hits, last_hit FROM moz_startup_pages;"));
   if (!stmt) {
     return;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2236,16 +2236,44 @@
       "n_buckets": 10,
       "description": "How long it takes to run the seer cleanup"
   },
   "SEER_CLEANUP_SCHEDULED": {
       "expires_in_version": "never",
       "kind": "boolean",
       "description": "Whether or not we actually try the cleanup method when we think about it"
   },
+  "SEER_LOAD_COUNT_IS_ZERO": {
+      "expires_in_version": "never",
+      "kind": "linear",
+      "high": "100",
+      "n_buckets": 50,
+      "description": "Number of times load count is zero"
+  },
+  "SEER_LOAD_COUNT_OVERFLOWS": {
+      "expires_in_version": "never",
+      "kind": "linear",
+      "high": "100",
+      "n_buckets": 50,
+      "description": "Number of times load count overflowed"
+  },
+  "SEER_STARTUP_COUNT_IS_ZERO": {
+      "expires_in_version": "never",
+      "kind": "linear",
+      "high": "100",
+      "n_buckets": 50,
+      "description": "Number of times startup count is zero"
+  },
+  "SEER_STARTUP_COUNT_OVERFLOWS": {
+      "expires_in_version": "never",
+      "kind": "linear",
+      "high": "100",
+      "n_buckets": 50,
+      "description": "Number of times startup count overflowed"
+  },
   "FIND_PLUGINS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "3000",
     "n_buckets": 10,
     "extended_statistics_ok": true,
     "description": "Time spent scanning filesystem for plugins (ms)"
   },