Bug 935482: New IOInterposer observer for recording main thread I/O to file; r=froydnj
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user