Bug 1099251 - make ChaosMode's behavior modifications more finely-grained selectable; r=roc
--- 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)