/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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/. */
#include <algorithm> #include <vector>
#include"IOInterposer.h"
#include"IOInterposerPrivate.h" #include"MainThreadIOLogger.h" #include"mozilla/Atomics.h" #include"mozilla/Mutex.h" #include"mozilla/RefPtr.h" #include"mozilla/StaticPtr.h" #include"mozilla/ThreadLocal.h" #include"nscore.h"// for NS_FREE_PERMANENT_DATA #if !defined(XP_WIN) # include "NSPRInterposer.h" #endif// !defined(XP_WIN) #include"nsXULAppAPI.h" #include"PoisonIOInterposer.h" #include"prenv.h"
namespace {
/** Find if a vector contains a specific element */ template <class T> bool VectorContains(const std::vector<T>& aVector, const T& aElement) { return std::find(aVector.begin(), aVector.end(), aElement) != aVector.end();
}
/** Remove element from a vector */ template <class T> void VectorRemove(std::vector<T>& aVector, const T& aElement) { typename std::vector<T>::iterator newEnd =
std::remove(aVector.begin(), aVector.end(), aElement);
aVector.erase(newEnd, aVector.end());
}
ObserverLists(ObserverLists const& aOther)
: mCreateObservers(aOther.mCreateObservers),
mReadObservers(aOther.mReadObservers),
mWriteObservers(aOther.mWriteObservers),
mFSyncObservers(aOther.mFSyncObservers),
mStatObservers(aOther.mStatObservers),
mCloseObservers(aOther.mCloseObservers),
mStageObservers(aOther.mStageObservers) {} // 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.
std::vector<mozilla::IOInterposeObserver*> mCreateObservers;
std::vector<mozilla::IOInterposeObserver*> mReadObservers;
std::vector<mozilla::IOInterposeObserver*> mWriteObservers;
std::vector<mozilla::IOInterposeObserver*> mFSyncObservers;
std::vector<mozilla::IOInterposeObserver*> mStatObservers;
std::vector<mozilla::IOInterposeObserver*> mCloseObservers;
std::vector<mozilla::IOInterposeObserver*> mStageObservers;
};
// Thread-safe list of observers, from which `PerThreadData` sources its own // local list when needed. class SourceList { public:
SourceList()
: mObservedOperations(mozilla::IOInterposeObserver::OpNone),
mIsEnabled(true) {
MOZ_COUNT_CTOR(SourceList);
}
ObserverLists* newLists = nullptr; if (mObserverLists) {
newLists = new ObserverLists(*mObserverLists);
} else {
newLists = new ObserverLists();
}
if (aOp & mozilla::IOInterposeObserver::OpCreateOrOpen) {
VectorRemove(newLists->mCreateObservers, aStaticObserver); if (newLists->mCreateObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations &
~mozilla::IOInterposeObserver::OpCreateOrOpen);
}
} if (aOp & mozilla::IOInterposeObserver::OpRead) {
VectorRemove(newLists->mReadObservers, aStaticObserver); if (newLists->mReadObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations & ~mozilla::IOInterposeObserver::OpRead);
}
} if (aOp & mozilla::IOInterposeObserver::OpWrite) {
VectorRemove(newLists->mWriteObservers, aStaticObserver); if (newLists->mWriteObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations & ~mozilla::IOInterposeObserver::OpWrite);
}
} if (aOp & mozilla::IOInterposeObserver::OpFSync) {
VectorRemove(newLists->mFSyncObservers, aStaticObserver); if (newLists->mFSyncObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations & ~mozilla::IOInterposeObserver::OpFSync);
}
} if (aOp & mozilla::IOInterposeObserver::OpStat) {
VectorRemove(newLists->mStatObservers, aStaticObserver); if (newLists->mStatObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations & ~mozilla::IOInterposeObserver::OpStat);
}
} if (aOp & mozilla::IOInterposeObserver::OpClose) {
VectorRemove(newLists->mCloseObservers, aStaticObserver); if (newLists->mCloseObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations & ~mozilla::IOInterposeObserver::OpClose);
}
} if (aOp & mozilla::IOInterposeObserver::OpNextStage) {
VectorRemove(newLists->mStageObservers, aStaticObserver); if (newLists->mStageObservers.empty()) {
mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
mObservedOperations & ~mozilla::IOInterposeObserver::OpNextStage);
}
}
mObserverLists = newLists;
mCurrentGeneration++;
}
void Update(PerThreadData& aPtd) { if (mCurrentGeneration == aPtd.GetCurrentGeneration()) { return;
} // If the generation counts don't match then we need to update the current // thread's observer list with the new source list.
mozilla::IOInterposer::AutoLock lock(mLock);
aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
}
inlinebool IsObservedOperation(mozilla::IOInterposeObserver::Operation aOp) { // This does not occur inside of a lock, so this makes no guarantees that // the observers are in sync with this. That is acceptable; it is not a // problem if we occasionally report more or less IO than is actually // occurring. return mIsEnabled && !!(mObservedOperations & aOp);
}
private:
RefPtr<const ObserverLists> mObserverLists MOZ_GUARDED_BY(mLock); // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked // (We want to monitor IO during shutdown). Furthermore, as we may have to // unregister observers during shutdown an OffTheBooksMutex is not an option // either, as its base calls into sDeadlockDetector which may be nullptr // during shutdown.
mozilla::IOInterposer::Mutex mLock; // Flags tracking which operations are being observed
mozilla::Atomic<mozilla::IOInterposeObserver::Operation,
mozilla::MemoryOrdering::Relaxed>
mObservedOperations; // Used for quickly disabling everything by IOInterposer::Disable()
mozilla::Atomic<bool> mIsEnabled; // Used to inform threads that the source observer list has changed
mozilla::Atomic<uint32_t> mCurrentGeneration;
};
// Special observation used by IOInterposer::EnteringNextStage() class NextStageObservation : public mozilla::IOInterposeObserver::Observation { public:
NextStageObservation()
: mozilla::IOInterposeObserver::Observation(
mozilla::IOInterposeObserver::OpNextStage, "IOInterposer", false) {
mStart = mozilla::TimeStamp::Now();
mEnd = mStart;
}
};
// List of observers registered static mozilla::StaticAutoPtr<SourceList> sSourceList; static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData; staticbool sThreadLocalDataInitialized;
constchar* 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";
}
}
bool IOInterposer::Init() { // Don't initialize twice... if (sSourceList) { returntrue;
} if (!sThreadLocalData.init()) { returnfalse;
}
sThreadLocalDataInitialized = true; bool isMainThread = true;
RegisterCurrentThread(isMainThread);
sSourceList = new SourceList();
MainThreadIOLogger::Init();
// Now we initialize the various interposers depending on platform
// Under certain conditions it may be unsafe to initialize PoisonIOInterposer, // such as when a background thread is already running. We set this variable // elsewhere when such a condition applies. if (!PR_GetEnv("MOZ_DISABLE_POISON_IO_INTERPOSER")) {
InitPoisonIOInterposer();
}
// We don't hook NSPR on Windows because PoisonIOInterposer captures a // superset of the former's events. #if !defined(XP_WIN)
InitNSPRIOInterposing(); #endif returntrue;
}
bool IOInterposeObserver::IsMainThread() { if (!sThreadLocalDataInitialized) { returnfalse;
}
PerThreadData* ptd = sThreadLocalData.get(); if (!ptd) { returnfalse;
} return ptd->IsMainThread();
}
void IOInterposer::Clear() { /* Clear() is a no-op on release builds so that we may continue to trap I/O until process termination. In leak-checking builds, we need to shut down
IOInterposer so that all references are properly released. */ #ifdef NS_FREE_PERMANENT_DATA
UnregisterCurrentThread();
sSourceList = nullptr; #endif
}
void IOInterposer::Disable() { if (!sSourceList) { return;
}
sSourceList->Disable();
}
void IOInterposer::Enable() { if (!sSourceList) { return;
}
sSourceList->Enable();
}
void IOInterposer::Report(IOInterposeObserver::Observation& aObservation) {
PerThreadData* ptd = sThreadLocalData.get(); if (!ptd) { // In this case the current thread is not registered with IOInterposer. // Alternatively we could take the slow path and just lock everything if // we're not registered. That could potentially perform poorly, though. return;
}
if (!sSourceList) { // If there is no longer a source list then we should clear the local one.
ptd->ClearObserverLists(); return;
}
sSourceList->Update(*ptd);
// Don't try to report if there's nobody listening. if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) { return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.