Bug 935482: New IOInterposer observer for recording main thread I/O to file; r=froydnj

This commit is contained in:
Aaron Klotz
2014-04-19 14:28:02 -06:00
parent a5e8596100
commit 0c4a7687a4
9 changed files with 563 additions and 136 deletions

View File

@@ -7,6 +7,8 @@
#include "IOInterposer.h"
#include "IOInterposerPrivate.h"
#include "MainThreadIOLogger.h"
#include "mozilla/Atomics.h"
#include "mozilla/Mutex.h"
#if defined(MOZILLA_INTERNAL_API)
@@ -47,7 +49,7 @@ void VectorRemove(std::vector<T>& vector, const T& element)
}
/** Lists of Observers */
struct ObserverLists : public AtomicRefCounted<ObserverLists>
struct ObserverLists : public mozilla::AtomicRefCounted<ObserverLists>
{
ObserverLists()
{
@@ -60,9 +62,10 @@ struct ObserverLists : public AtomicRefCounted<ObserverLists>
, mFSyncObservers(aOther.mFSyncObservers)
, mStatObservers(aOther.mStatObservers)
, mCloseObservers(aOther.mCloseObservers)
, mStageObservers(aOther.mStageObservers)
{
}
// Lists of observers for read, write and fsync events respectively
// Lists of observers for I/O events.
// These are implemented as vectors since they are allowed to survive gecko,
// without reporting leaks. This is necessary for the IOInterposer to be used
// for late-write checks.
@@ -72,24 +75,7 @@ struct ObserverLists : public AtomicRefCounted<ObserverLists>
std::vector<IOInterposeObserver*> mFSyncObservers;
std::vector<IOInterposeObserver*> mStatObservers;
std::vector<IOInterposeObserver*> mCloseObservers;
};
/**
* A quick and dirty RAII class to automatically lock a PRLock
*/
class AutoPRLock
{
PRLock* mLock;
public:
AutoPRLock(PRLock* aLock)
: mLock(aLock)
{
PR_Lock(aLock);
}
~AutoPRLock()
{
PR_Unlock(mLock);
}
std::vector<IOInterposeObserver*> mStageObservers;
};
class PerThreadData
@@ -144,6 +130,11 @@ public:
observers = &mObserverLists->mCloseObservers;
}
break;
case IOInterposeObserver::OpNextStage:
{
observers = &mObserverLists->mStageObservers;
}
break;
default:
{
// Invalid IO operation, see documentation comment for
@@ -194,16 +185,13 @@ class MasterList
{
public:
MasterList()
: mLock(PR_NewLock())
, mObservedOperations(IOInterposeObserver::OpNone)
: mObservedOperations(IOInterposeObserver::OpNone)
, mIsEnabled(true)
{
}
~MasterList()
{
PR_DestroyLock(mLock);
mLock = nullptr;
}
inline void
@@ -215,7 +203,7 @@ public:
void
Register(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
{
AutoPRLock lock(mLock);
IOInterposer::AutoLock lock(mLock);
ObserverLists* newLists = nullptr;
if (mObserverLists) {
@@ -249,6 +237,10 @@ public:
!VectorContains(newLists->mCloseObservers, aObserver)) {
newLists->mCloseObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpNextStage &&
!VectorContains(newLists->mStageObservers, aObserver)) {
newLists->mStageObservers.push_back(aObserver);
}
mObserverLists = newLists;
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations | aOp);
@@ -259,7 +251,7 @@ public:
void
Unregister(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
{
AutoPRLock lock(mLock);
IOInterposer::AutoLock lock(mLock);
ObserverLists* newLists = nullptr;
if (mObserverLists) {
@@ -311,6 +303,13 @@ public:
(mObservedOperations & ~IOInterposeObserver::OpClose);
}
}
if (aOp & IOInterposeObserver::OpNextStage) {
VectorRemove(newLists->mStageObservers, aObserver);
if (newLists->mStageObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations & ~IOInterposeObserver::OpNextStage);
}
}
mObserverLists = newLists;
mCurrentGeneration++;
}
@@ -323,7 +322,7 @@ public:
}
// If the generation counts don't match then we need to update the current
// thread's observer list with the new master list.
AutoPRLock lock(mLock);
IOInterposer::AutoLock lock(mLock);
aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
}
@@ -345,7 +344,7 @@ private:
// unregister observers during shutdown an OffTheBooksMutex is not an option
// either, as its base calls into sDeadlockDetector which may be nullptr
// during shutdown.
PRLock* mLock;
IOInterposer::Mutex mLock;
// Flags tracking which operations are being observed
IOInterposeObserver::Operation mObservedOperations;
// Used for quickly disabling everything by IOInterposer::Disable()
@@ -354,6 +353,18 @@ private:
Atomic<uint32_t> mCurrentGeneration;
};
// Special observation used by IOInterposer::EnteringNextStage()
class NextStageObservation : public IOInterposeObserver::Observation
{
public:
NextStageObservation()
: IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
"IOInterposer", false)
{
mStart = TimeStamp::Now();
}
};
// List of observers registered
static StaticAutoPtr<MasterList> sMasterList;
static ThreadLocal<PerThreadData*> sThreadLocalData;
@@ -384,6 +395,29 @@ IOInterposeObserver::Observation::Observation(Operation aOperation,
{
}
const char*
IOInterposeObserver::Observation::ObservedOperationString() const
{
switch(mOperation) {
case OpCreateOrOpen:
return "create/open";
case OpRead:
return "read";
case OpWrite:
return "write";
case OpFSync:
return "fsync";
case OpStat:
return "stat";
case OpClose:
return "close";
case OpNextStage:
return "NextStage";
default:
return "unknown";
}
}
void
IOInterposeObserver::Observation::Report()
{
@@ -393,7 +427,7 @@ IOInterposeObserver::Observation::Report()
}
}
/* static */ bool
bool
IOInterposer::Init()
{
// Don't initialize twice...
@@ -412,6 +446,8 @@ IOInterposer::Init()
RegisterCurrentThread(isMainThread);
sMasterList = new MasterList();
MainThreadIOLogger::Init();
// Now we initialize the various interposers depending on platform
InitPoisonIOInterposer();
// We don't hook NSPR on Windows because PoisonIOInterposer captures a
@@ -422,7 +458,7 @@ IOInterposer::Init()
return true;
}
/* static */ bool
bool
IOInterposeObserver::IsMainThread()
{
if (!sThreadLocalData.initialized()) {
@@ -435,13 +471,13 @@ IOInterposeObserver::IsMainThread()
return ptd->IsMainThread();
}
/* static */ void
void
IOInterposer::Clear()
{
sMasterList = nullptr;
}
/* static */ void
void
IOInterposer::Disable()
{
if (!sMasterList) {
@@ -450,7 +486,7 @@ IOInterposer::Disable()
sMasterList->Disable();
}
/* static */ void
void
IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
{
MOZ_ASSERT(sMasterList);
@@ -475,13 +511,13 @@ IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
ptd->CallObservers(aObservation);
}
/* static */ bool
bool
IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
{
return sMasterList && sMasterList->IsObservedOperation(aOp);
}
/* static */ void
void
IOInterposer::Register(IOInterposeObserver::Operation aOp,
IOInterposeObserver* aObserver)
{
@@ -493,7 +529,7 @@ IOInterposer::Register(IOInterposeObserver::Operation aOp,
sMasterList->Register(aOp, aObserver);
}
/* static */ void
void
IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
IOInterposeObserver* aObserver)
{
@@ -504,7 +540,7 @@ IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
sMasterList->Unregister(aOp, aObserver);
}
/* static */ void
void
IOInterposer::RegisterCurrentThread(bool aIsMainThread)
{
if (!sThreadLocalData.initialized()) {
@@ -515,7 +551,7 @@ IOInterposer::RegisterCurrentThread(bool aIsMainThread)
sThreadLocalData.set(curThreadData);
}
/* static */ void
void
IOInterposer::UnregisterCurrentThread()
{
if (!sThreadLocalData.initialized()) {
@@ -527,3 +563,13 @@ IOInterposer::UnregisterCurrentThread()
delete curThreadData;
}
void
IOInterposer::EnteringNextStage()
{
if (!sMasterList) {
return;
}
NextStageObservation observation;
Report(observation);
}