Bug 987248 - Prevent divide-by-zero in seer. r=mcmanus, a=sledru
authorNicholas Hurley <hurley@todesschaf.org>
Mon, 07 Apr 2014 12:45:45 -0700
changeset 183689 afdcb5d5d7cc
parent 183688 14b8222e1a24
child 183690 2d58340206f4
push id3447
push userryanvm@gmail.com
push date2014-04-09 18:07 +0000
treeherdermozilla-beta@44a94313968a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus, sledru
bugs987248
milestone29.0
Bug 987248 - Prevent divide-by-zero in seer. r=mcmanus, a=sledru
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
@@ -2250,16 +2250,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)"
   },