Bug 1099251 - make ChaosMode's behavior modifications more finely-grained selectable; r=roc
authorNathan Froyd <froydnj@mozilla.com>
Fri, 14 Nov 2014 13:12:51 -0500
changeset 216500 f8863cbd49cbabec9baf8749e0ee2cad4c108657
parent 216499 b8919fcbf6b2246428024d72b938ed9a5f8e9c92
child 216501 8e604ebfc31388fefc9a8647bde4734e6f97eb87
push id27852
push userkwierso@gmail.com
push dateThu, 20 Nov 2014 02:35:33 +0000
treeherdermozilla-central@dd71e7d2b345 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1099251
milestone36.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 1099251 - make ChaosMode's behavior modifications more finely-grained selectable; r=roc
mfbt/ChaosMode.h
netwerk/base/src/nsSocketTransportService2.cpp
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnectionMgr.cpp
toolkit/xre/nsAppRunner.cpp
xpcom/glue/pldhash.cpp
xpcom/threads/TimerThread.cpp
xpcom/threads/nsEventQueue.cpp
xpcom/threads/nsThread.cpp
--- a/mfbt/ChaosMode.h
+++ b/mfbt/ChaosMode.h
@@ -2,33 +2,54 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ChaosMode_h
 #define mozilla_ChaosMode_h
 
+#include "mozilla/EnumSet.h"
+
 #include <stdint.h>
 #include <stdlib.h>
 
 namespace mozilla {
 
 /**
  * When "chaos mode" is activated, code that makes implicitly nondeterministic
  * choices is encouraged to make random and extreme choices, to test more
  * code paths and uncover bugs.
  */
 class ChaosMode
 {
 public:
-  static bool isActive()
+  enum ChaosFeature {
+    None = 0x0,
+    // Altering thread scheduling.
+    ThreadScheduling = 0x1,
+    // Altering network request scheduling.
+    NetworkScheduling = 0x2,
+    // Altering timer scheduling.
+    TimerScheduling = 0x4,
+    // Read and write less-than-requested amounts.
+    IOAmounts = 0x8,
+    // Iterate over hash tables in random order.
+    HashTableIteration = 0x10,
+    Any = 0xffffffff,
+  };
+
+private:
+  // Change this to any non-None value to activate ChaosMode.
+  static const ChaosFeature sChaosFeatures = None;
+
+public:
+  static bool isActive(ChaosFeature aFeature)
   {
-    // Flip this to true to activate chaos mode
-    return false;
+    return sChaosFeatures & aFeature;
   }
 
   /**
    * Returns a somewhat (but not uniformly) random uint32_t < aBound.
    * Not to be used for anything except ChaosMode, since it's not very random.
    */
   static uint32_t randomUint32LessThan(uint32_t aBound)
   {
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -221,17 +221,17 @@ nsSocketTransportService::AddToPollList(
         SOCKET_LOG(("  Active List size of %d met\n", mActiveCount));
         if (!GrowActiveList()) {
             NS_ERROR("too many active sockets");
             return NS_ERROR_OUT_OF_MEMORY;
         }
     }
     
     uint32_t newSocketIndex = mActiveCount;
-    if (ChaosMode::isActive()) {
+    if (ChaosMode::isActive(ChaosMode::NetworkScheduling)) {
       newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
       PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
               mActiveCount - newSocketIndex);
       PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
               mActiveCount - newSocketIndex);
     }
     mActiveList[newSocketIndex] = *sock;
     mActiveCount++;
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -1639,17 +1639,18 @@ nsHttpConnection::OnWriteSegment(char *b
     if (count == 0) {
         // some WriteSegments implementations will erroneously call the reader
         // to provide 0 bytes worth of data.  we must protect against this case
         // or else we'd end up closing the socket prematurely.
         NS_ERROR("bad WriteSegments implementation");
         return NS_ERROR_FAILURE; // stop iterating
     }
 
-    if (ChaosMode::isActive() && ChaosMode::randomUint32LessThan(2)) {
+    if (ChaosMode::isActive(ChaosMode::IOAmounts) &&
+        ChaosMode::randomUint32LessThan(2)) {
         // read 1...count bytes
         count = ChaosMode::randomUint32LessThan(count) + 1;
     }
 
     nsresult rv = mSocketIn->Read(buf, count, countWritten);
     if (NS_FAILED(rv))
         mSocketInCondition = rv;
     else if (*countWritten == 0)
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -50,17 +50,17 @@ InsertTransactionSorted(nsTArray<nsHttpT
 {
     // insert into queue with smallest valued number first.  search in reverse
     // order under the assumption that many of the existing transactions will
     // have the same priority (usually 0).
 
     for (int32_t i=pendingQ.Length()-1; i>=0; --i) {
         nsHttpTransaction *t = pendingQ[i];
         if (trans->Priority() >= t->Priority()) {
-            if (ChaosMode::isActive()) {
+	  if (ChaosMode::isActive(ChaosMode::NetworkScheduling)) {
                 int32_t samePriorityCount;
                 for (samePriorityCount = 0; i - samePriorityCount >= 0; ++samePriorityCount) {
                     if (pendingQ[i - samePriorityCount]->Priority() != trans->Priority()) {
                         break;
                     }
                 }
                 // skip over 0...all of the elements with the same priority.
                 i -= ChaosMode::randomUint32LessThan(samePriorityCount + 1);
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2979,17 +2979,17 @@ int
 XREMain::XRE_mainInit(bool* aExitFlag)
 {
   if (!aExitFlag)
     return 1;
   *aExitFlag = false;
 
   StartupTimeline::Record(StartupTimeline::MAIN);
 
-  if (ChaosMode::isActive()) {
+  if (ChaosMode::isActive(ChaosMode::Any)) {
     printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
   }
 
   nsresult rv;
   ArgResult ar;
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
--- a/xpcom/glue/pldhash.cpp
+++ b/xpcom/glue/pldhash.cpp
@@ -702,17 +702,17 @@ PLDHashTable::Enumerate(PLDHashEnumerato
   // and ::NextEntry methods below in this file.
   char* entryAddr = mEntryStore;
   uint32_t capacity = Capacity();
   uint32_t tableSize = capacity * mEntrySize;
   char* entryLimit = entryAddr + tableSize;
   uint32_t i = 0;
   bool didRemove = false;
 
-  if (ChaosMode::isActive()) {
+  if (ChaosMode::isActive(ChaosMode::HashTableIteration)) {
     // Start iterating at a random point in the hashtable. It would be
     // even more chaotic to iterate in fully random order, but that's a lot
     // more work.
     entryAddr += ChaosMode::randomUint32LessThan(capacity) * mEntrySize;
     if (entryAddr >= entryLimit) {
       entryAddr -= tableSize;
     }
   }
@@ -850,17 +850,17 @@ PLDHashTable::Iterator::Iterator(const P
   // The following code is taken from, and should be kept in sync with, the
   // PLDHashTable::Enumerate method above. The variables i and entryAddr (which
   // vary over the course of the for loop) are converted into mEntryOffset and
   // mEntryAddr, respectively.
   uint32_t capacity = mTable->Capacity();
   uint32_t tableSize = capacity * mTable->EntrySize();
   char* entryLimit = mEntryAddr + tableSize;
 
-  if (ChaosMode::isActive()) {
+  if (ChaosMode::isActive(ChaosMode::HashTableIteration)) {
     // Start iterating at a random point in the hashtable. It would be
     // even more chaotic to iterate in fully random order, but that's a lot
     // more work.
     mEntryAddr += ChaosMode::randomUint32LessThan(capacity) * mTable->mEntrySize;
     if (mEntryAddr >= entryLimit) {
       mEntryAddr -= tableSize;
     }
   }
--- a/xpcom/threads/TimerThread.cpp
+++ b/xpcom/threads/TimerThread.cpp
@@ -232,17 +232,17 @@ TimerThread::Run()
     // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
     PRIntervalTime waitFor;
     bool forceRunThisTimer = forceRunNextTimer;
     forceRunNextTimer = false;
 
     if (mSleeping) {
       // Sleep for 0.1 seconds while not firing timers.
       uint32_t milliseconds = 100;
-      if (ChaosMode::isActive()) {
+      if (ChaosMode::isActive(ChaosMode::TimerScheduling)) {
         milliseconds = ChaosMode::randomUint32LessThan(200);
       }
       waitFor = PR_MillisecondsToInterval(milliseconds);
     } else {
       waitFor = PR_INTERVAL_NO_TIMEOUT;
       TimeStamp now = TimeStamp::Now();
       nsTimerImpl* timer = nullptr;
 
@@ -319,17 +319,17 @@ TimerThread::Run()
         // is due now or overdue.
         //
         // Note that we can only sleep for integer values of a certain
         // resolution. We use halfMicrosecondsIntervalResolution, calculated
         // before, to do the optimal rounding (i.e., of how to decide what
         // interval is so small we should not wait at all).
         double microseconds = (timeout - now).ToMilliseconds() * 1000;
 
-        if (ChaosMode::isActive()) {
+        if (ChaosMode::isActive(ChaosMode::TimerScheduling)) {
           // The mean value of sFractions must be 1 to ensure that
           // the average of a long sequence of timeouts converges to the
           // actual sum of their times.
           static const float sFractions[] = {
             0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
           };
           microseconds *=
             sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
--- a/xpcom/threads/nsEventQueue.cpp
+++ b/xpcom/threads/nsEventQueue.cpp
@@ -85,17 +85,17 @@ nsEventQueue::GetEvent(bool aMayWait, ns
 }
 
 void
 nsEventQueue::PutEvent(nsIRunnable* aRunnable)
 {
   // Avoid calling AddRef+Release while holding our monitor.
   nsRefPtr<nsIRunnable> event(aRunnable);
 
-  if (ChaosMode::isActive()) {
+  if (ChaosMode::isActive(ChaosMode::ThreadScheduling)) {
     // With probability 0.5, yield so other threads have a chance to
     // dispatch events to this queue first.
     if (ChaosMode::randomUint32LessThan(2)) {
       PR_Sleep(PR_INTERVAL_NO_WAIT);
     }
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -280,17 +280,17 @@ private:
   nsThreadShutdownContext* mShutdownContext;
 };
 
 //-----------------------------------------------------------------------------
 
 static void
 SetupCurrentThreadForChaosMode()
 {
-  if (!ChaosMode::isActive()) {
+  if (!ChaosMode::isActive(ChaosMode::ThreadScheduling)) {
     return;
   }
 
 #ifdef XP_LINUX
   // PR_SetThreadPriority doesn't really work since priorities >
   // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
   // setpriority(2) to set random 'nice values'. In regular Linux this is only
   // a dynamic adjustment so it still doesn't really do what we want, but tools
@@ -883,17 +883,17 @@ nsThread::SetPriority(int32_t aPriority)
   } else if (mPriority < PRIORITY_NORMAL) {
     pri = PR_PRIORITY_HIGH;
   } else if (mPriority > PRIORITY_NORMAL) {
     pri = PR_PRIORITY_LOW;
   } else {
     pri = PR_PRIORITY_NORMAL;
   }
   // If chaos mode is active, retain the randomly chosen priority
-  if (!ChaosMode::isActive()) {
+  if (!ChaosMode::isActive(ChaosMode::ThreadScheduling)) {
     PR_SetThreadPriority(mThread, pri);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThread::AdjustPriority(int32_t aDelta)