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 idunknown
push userunknown
push dateunknown
reviewersroc
bugs1099251
milestone36.0a1
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)