1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/build/IOInterposer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,575 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <algorithm> 1.9 +#include <vector> 1.10 + 1.11 +#include "IOInterposer.h" 1.12 + 1.13 +#include "IOInterposerPrivate.h" 1.14 +#include "MainThreadIOLogger.h" 1.15 +#include "mozilla/Atomics.h" 1.16 +#include "mozilla/Mutex.h" 1.17 +#if defined(MOZILLA_INTERNAL_API) 1.18 +// We need to undefine MOZILLA_INTERNAL_API for RefPtr.h because IOInterposer 1.19 +// does not clean up its data before shutdown. 1.20 +#undef MOZILLA_INTERNAL_API 1.21 +#include "mozilla/RefPtr.h" 1.22 +#define MOZILLA_INTERNAL_API 1.23 +#else 1.24 +#include "mozilla/RefPtr.h" 1.25 +#endif // defined(MOZILLA_INTERNAL_API) 1.26 +#include "mozilla/StaticPtr.h" 1.27 +#include "mozilla/ThreadLocal.h" 1.28 +#if !defined(XP_WIN) 1.29 +#include "NSPRInterposer.h" 1.30 +#endif // !defined(XP_WIN) 1.31 +#include "nsXULAppAPI.h" 1.32 +#include "PoisonIOInterposer.h" 1.33 + 1.34 +using namespace mozilla; 1.35 + 1.36 +namespace { 1.37 + 1.38 +/** Find if a vector contains a specific element */ 1.39 +template<class T> 1.40 +bool VectorContains(const std::vector<T>& vector, const T& element) 1.41 +{ 1.42 + return std::find(vector.begin(), vector.end(), element) != vector.end(); 1.43 +} 1.44 + 1.45 +/** Remove element from a vector */ 1.46 +template<class T> 1.47 +void VectorRemove(std::vector<T>& vector, const T& element) 1.48 +{ 1.49 + typename std::vector<T>::iterator newEnd = std::remove(vector.begin(), 1.50 + vector.end(), element); 1.51 + vector.erase(newEnd, vector.end()); 1.52 +} 1.53 + 1.54 +/** Lists of Observers */ 1.55 +struct ObserverLists : public mozilla::AtomicRefCounted<ObserverLists> 1.56 +{ 1.57 + ObserverLists() 1.58 + { 1.59 + } 1.60 + 1.61 + ObserverLists(ObserverLists const & aOther) 1.62 + : mCreateObservers(aOther.mCreateObservers) 1.63 + , mReadObservers(aOther.mReadObservers) 1.64 + , mWriteObservers(aOther.mWriteObservers) 1.65 + , mFSyncObservers(aOther.mFSyncObservers) 1.66 + , mStatObservers(aOther.mStatObservers) 1.67 + , mCloseObservers(aOther.mCloseObservers) 1.68 + , mStageObservers(aOther.mStageObservers) 1.69 + { 1.70 + } 1.71 + // Lists of observers for I/O events. 1.72 + // These are implemented as vectors since they are allowed to survive gecko, 1.73 + // without reporting leaks. This is necessary for the IOInterposer to be used 1.74 + // for late-write checks. 1.75 + std::vector<IOInterposeObserver*> mCreateObservers; 1.76 + std::vector<IOInterposeObserver*> mReadObservers; 1.77 + std::vector<IOInterposeObserver*> mWriteObservers; 1.78 + std::vector<IOInterposeObserver*> mFSyncObservers; 1.79 + std::vector<IOInterposeObserver*> mStatObservers; 1.80 + std::vector<IOInterposeObserver*> mCloseObservers; 1.81 + std::vector<IOInterposeObserver*> mStageObservers; 1.82 +}; 1.83 + 1.84 +class PerThreadData 1.85 +{ 1.86 +public: 1.87 + PerThreadData(bool aIsMainThread = false) 1.88 + : mIsMainThread(aIsMainThread) 1.89 + , mIsHandlingObservation(false) 1.90 + , mCurrentGeneration(0) 1.91 + { 1.92 + } 1.93 + 1.94 + void 1.95 + CallObservers(IOInterposeObserver::Observation& aObservation) 1.96 + { 1.97 + // Prevent recursive reporting. 1.98 + if (mIsHandlingObservation) { 1.99 + return; 1.100 + } 1.101 + 1.102 + mIsHandlingObservation = true; 1.103 + // Decide which list of observers to inform 1.104 + std::vector<IOInterposeObserver*>* observers = nullptr; 1.105 + switch (aObservation.ObservedOperation()) { 1.106 + case IOInterposeObserver::OpCreateOrOpen: 1.107 + { 1.108 + observers = &mObserverLists->mCreateObservers; 1.109 + } 1.110 + break; 1.111 + case IOInterposeObserver::OpRead: 1.112 + { 1.113 + observers = &mObserverLists->mReadObservers; 1.114 + } 1.115 + break; 1.116 + case IOInterposeObserver::OpWrite: 1.117 + { 1.118 + observers = &mObserverLists->mWriteObservers; 1.119 + } 1.120 + break; 1.121 + case IOInterposeObserver::OpFSync: 1.122 + { 1.123 + observers = &mObserverLists->mFSyncObservers; 1.124 + } 1.125 + break; 1.126 + case IOInterposeObserver::OpStat: 1.127 + { 1.128 + observers = &mObserverLists->mStatObservers; 1.129 + } 1.130 + break; 1.131 + case IOInterposeObserver::OpClose: 1.132 + { 1.133 + observers = &mObserverLists->mCloseObservers; 1.134 + } 1.135 + break; 1.136 + case IOInterposeObserver::OpNextStage: 1.137 + { 1.138 + observers = &mObserverLists->mStageObservers; 1.139 + } 1.140 + break; 1.141 + default: 1.142 + { 1.143 + // Invalid IO operation, see documentation comment for 1.144 + // IOInterposer::Report() 1.145 + MOZ_ASSERT(false); 1.146 + // Just ignore it in non-debug builds. 1.147 + return; 1.148 + } 1.149 + } 1.150 + MOZ_ASSERT(observers); 1.151 + 1.152 + // Inform observers 1.153 + for (std::vector<IOInterposeObserver*>::iterator i = observers->begin(), 1.154 + e = observers->end(); i != e; ++i) 1.155 + { 1.156 + (*i)->Observe(aObservation); 1.157 + } 1.158 + mIsHandlingObservation = false; 1.159 + } 1.160 + 1.161 + inline uint32_t 1.162 + GetCurrentGeneration() const 1.163 + { 1.164 + return mCurrentGeneration; 1.165 + } 1.166 + 1.167 + inline bool 1.168 + IsMainThread() const 1.169 + { 1.170 + return mIsMainThread; 1.171 + } 1.172 + 1.173 + inline void 1.174 + SetObserverLists(uint32_t aNewGeneration, RefPtr<ObserverLists>& aNewLists) 1.175 + { 1.176 + mCurrentGeneration = aNewGeneration; 1.177 + mObserverLists = aNewLists; 1.178 + } 1.179 + 1.180 +private: 1.181 + bool mIsMainThread; 1.182 + bool mIsHandlingObservation; 1.183 + uint32_t mCurrentGeneration; 1.184 + RefPtr<ObserverLists> mObserverLists; 1.185 +}; 1.186 + 1.187 +class MasterList 1.188 +{ 1.189 +public: 1.190 + MasterList() 1.191 + : mObservedOperations(IOInterposeObserver::OpNone) 1.192 + , mIsEnabled(true) 1.193 + { 1.194 + } 1.195 + 1.196 + ~MasterList() 1.197 + { 1.198 + } 1.199 + 1.200 + inline void 1.201 + Disable() 1.202 + { 1.203 + mIsEnabled = false; 1.204 + } 1.205 + 1.206 + void 1.207 + Register(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver) 1.208 + { 1.209 + IOInterposer::AutoLock lock(mLock); 1.210 + 1.211 + ObserverLists* newLists = nullptr; 1.212 + if (mObserverLists) { 1.213 + newLists = new ObserverLists(*mObserverLists); 1.214 + } else { 1.215 + newLists = new ObserverLists(); 1.216 + } 1.217 + // You can register to observe multiple types of observations 1.218 + // but you'll never be registered twice for the same observations. 1.219 + if (aOp & IOInterposeObserver::OpCreateOrOpen && 1.220 + !VectorContains(newLists->mCreateObservers, aObserver)) { 1.221 + newLists->mCreateObservers.push_back(aObserver); 1.222 + } 1.223 + if (aOp & IOInterposeObserver::OpRead && 1.224 + !VectorContains(newLists->mReadObservers, aObserver)) { 1.225 + newLists->mReadObservers.push_back(aObserver); 1.226 + } 1.227 + if (aOp & IOInterposeObserver::OpWrite && 1.228 + !VectorContains(newLists->mWriteObservers, aObserver)) { 1.229 + newLists->mWriteObservers.push_back(aObserver); 1.230 + } 1.231 + if (aOp & IOInterposeObserver::OpFSync && 1.232 + !VectorContains(newLists->mFSyncObservers, aObserver)) { 1.233 + newLists->mFSyncObservers.push_back(aObserver); 1.234 + } 1.235 + if (aOp & IOInterposeObserver::OpStat && 1.236 + !VectorContains(newLists->mStatObservers, aObserver)) { 1.237 + newLists->mStatObservers.push_back(aObserver); 1.238 + } 1.239 + if (aOp & IOInterposeObserver::OpClose && 1.240 + !VectorContains(newLists->mCloseObservers, aObserver)) { 1.241 + newLists->mCloseObservers.push_back(aObserver); 1.242 + } 1.243 + if (aOp & IOInterposeObserver::OpNextStage && 1.244 + !VectorContains(newLists->mStageObservers, aObserver)) { 1.245 + newLists->mStageObservers.push_back(aObserver); 1.246 + } 1.247 + mObserverLists = newLists; 1.248 + mObservedOperations = (IOInterposeObserver::Operation) 1.249 + (mObservedOperations | aOp); 1.250 + 1.251 + mCurrentGeneration++; 1.252 + } 1.253 + 1.254 + void 1.255 + Unregister(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver) 1.256 + { 1.257 + IOInterposer::AutoLock lock(mLock); 1.258 + 1.259 + ObserverLists* newLists = nullptr; 1.260 + if (mObserverLists) { 1.261 + newLists = new ObserverLists(*mObserverLists); 1.262 + } else { 1.263 + newLists = new ObserverLists(); 1.264 + } 1.265 + 1.266 + if (aOp & IOInterposeObserver::OpCreateOrOpen) { 1.267 + VectorRemove(newLists->mCreateObservers, aObserver); 1.268 + if (newLists->mCreateObservers.empty()) { 1.269 + mObservedOperations = (IOInterposeObserver::Operation) 1.270 + (mObservedOperations & 1.271 + ~IOInterposeObserver::OpCreateOrOpen); 1.272 + } 1.273 + } 1.274 + if (aOp & IOInterposeObserver::OpRead) { 1.275 + VectorRemove(newLists->mReadObservers, aObserver); 1.276 + if (newLists->mReadObservers.empty()) { 1.277 + mObservedOperations = (IOInterposeObserver::Operation) 1.278 + (mObservedOperations & ~IOInterposeObserver::OpRead); 1.279 + } 1.280 + } 1.281 + if (aOp & IOInterposeObserver::OpWrite) { 1.282 + VectorRemove(newLists->mWriteObservers, aObserver); 1.283 + if (newLists->mWriteObservers.empty()) { 1.284 + mObservedOperations = (IOInterposeObserver::Operation) 1.285 + (mObservedOperations & ~IOInterposeObserver::OpWrite); 1.286 + } 1.287 + } 1.288 + if (aOp & IOInterposeObserver::OpFSync) { 1.289 + VectorRemove(newLists->mFSyncObservers, aObserver); 1.290 + if (newLists->mFSyncObservers.empty()) { 1.291 + mObservedOperations = (IOInterposeObserver::Operation) 1.292 + (mObservedOperations & ~IOInterposeObserver::OpFSync); 1.293 + } 1.294 + } 1.295 + if (aOp & IOInterposeObserver::OpStat) { 1.296 + VectorRemove(newLists->mStatObservers, aObserver); 1.297 + if (newLists->mStatObservers.empty()) { 1.298 + mObservedOperations = (IOInterposeObserver::Operation) 1.299 + (mObservedOperations & ~IOInterposeObserver::OpStat); 1.300 + } 1.301 + } 1.302 + if (aOp & IOInterposeObserver::OpClose) { 1.303 + VectorRemove(newLists->mCloseObservers, aObserver); 1.304 + if (newLists->mCloseObservers.empty()) { 1.305 + mObservedOperations = (IOInterposeObserver::Operation) 1.306 + (mObservedOperations & ~IOInterposeObserver::OpClose); 1.307 + } 1.308 + } 1.309 + if (aOp & IOInterposeObserver::OpNextStage) { 1.310 + VectorRemove(newLists->mStageObservers, aObserver); 1.311 + if (newLists->mStageObservers.empty()) { 1.312 + mObservedOperations = (IOInterposeObserver::Operation) 1.313 + (mObservedOperations & ~IOInterposeObserver::OpNextStage); 1.314 + } 1.315 + } 1.316 + mObserverLists = newLists; 1.317 + mCurrentGeneration++; 1.318 + } 1.319 + 1.320 + void 1.321 + Update(PerThreadData &aPtd) 1.322 + { 1.323 + if (mCurrentGeneration == aPtd.GetCurrentGeneration()) { 1.324 + return; 1.325 + } 1.326 + // If the generation counts don't match then we need to update the current 1.327 + // thread's observer list with the new master list. 1.328 + IOInterposer::AutoLock lock(mLock); 1.329 + aPtd.SetObserverLists(mCurrentGeneration, mObserverLists); 1.330 + } 1.331 + 1.332 + inline bool 1.333 + IsObservedOperation(IOInterposeObserver::Operation aOp) 1.334 + { 1.335 + // The quick reader may observe that no locks are being employed here, 1.336 + // hence the result of the operations is truly undefined. However, most 1.337 + // computers will usually return either true or false, which is good enough. 1.338 + // It is not a problem if we occasionally report more or less IO than is 1.339 + // actually occurring. 1.340 + return mIsEnabled && !!(mObservedOperations & aOp); 1.341 + } 1.342 + 1.343 +private: 1.344 + RefPtr<ObserverLists> mObserverLists; 1.345 + // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked 1.346 + // (We want to monitor IO during shutdown). Furthermore, as we may have to 1.347 + // unregister observers during shutdown an OffTheBooksMutex is not an option 1.348 + // either, as its base calls into sDeadlockDetector which may be nullptr 1.349 + // during shutdown. 1.350 + IOInterposer::Mutex mLock; 1.351 + // Flags tracking which operations are being observed 1.352 + IOInterposeObserver::Operation mObservedOperations; 1.353 + // Used for quickly disabling everything by IOInterposer::Disable() 1.354 + Atomic<bool> mIsEnabled; 1.355 + // Used to inform threads that the master observer list has changed 1.356 + Atomic<uint32_t> mCurrentGeneration; 1.357 +}; 1.358 + 1.359 +// Special observation used by IOInterposer::EnteringNextStage() 1.360 +class NextStageObservation : public IOInterposeObserver::Observation 1.361 +{ 1.362 +public: 1.363 + NextStageObservation() 1.364 + : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage, 1.365 + "IOInterposer", false) 1.366 + { 1.367 + mStart = TimeStamp::Now(); 1.368 + } 1.369 +}; 1.370 + 1.371 +// List of observers registered 1.372 +static StaticAutoPtr<MasterList> sMasterList; 1.373 +static ThreadLocal<PerThreadData*> sThreadLocalData; 1.374 +} // anonymous namespace 1.375 + 1.376 +IOInterposeObserver::Observation::Observation(Operation aOperation, 1.377 + const char* aReference, 1.378 + bool aShouldReport) 1.379 + : mOperation(aOperation) 1.380 + , mReference(aReference) 1.381 + , mShouldReport(IOInterposer::IsObservedOperation(aOperation) && 1.382 + aShouldReport) 1.383 +{ 1.384 + if (mShouldReport) { 1.385 + mStart = TimeStamp::Now(); 1.386 + } 1.387 +} 1.388 + 1.389 +IOInterposeObserver::Observation::Observation(Operation aOperation, 1.390 + const TimeStamp& aStart, 1.391 + const TimeStamp& aEnd, 1.392 + const char* aReference) 1.393 + : mOperation(aOperation) 1.394 + , mStart(aStart) 1.395 + , mEnd(aEnd) 1.396 + , mReference(aReference) 1.397 + , mShouldReport(false) 1.398 +{ 1.399 +} 1.400 + 1.401 +const char* 1.402 +IOInterposeObserver::Observation::ObservedOperationString() const 1.403 +{ 1.404 + switch(mOperation) { 1.405 + case OpCreateOrOpen: 1.406 + return "create/open"; 1.407 + case OpRead: 1.408 + return "read"; 1.409 + case OpWrite: 1.410 + return "write"; 1.411 + case OpFSync: 1.412 + return "fsync"; 1.413 + case OpStat: 1.414 + return "stat"; 1.415 + case OpClose: 1.416 + return "close"; 1.417 + case OpNextStage: 1.418 + return "NextStage"; 1.419 + default: 1.420 + return "unknown"; 1.421 + } 1.422 +} 1.423 + 1.424 +void 1.425 +IOInterposeObserver::Observation::Report() 1.426 +{ 1.427 + if (mShouldReport) { 1.428 + mEnd = TimeStamp::Now(); 1.429 + IOInterposer::Report(*this); 1.430 + } 1.431 +} 1.432 + 1.433 +bool 1.434 +IOInterposer::Init() 1.435 +{ 1.436 + // Don't initialize twice... 1.437 + if (sMasterList) { 1.438 + return true; 1.439 + } 1.440 + if (!sThreadLocalData.init()) { 1.441 + return false; 1.442 + } 1.443 +#if defined(XP_WIN) 1.444 + bool isMainThread = XRE_GetWindowsEnvironment() != 1.445 + WindowsEnvironmentType_Metro; 1.446 +#else 1.447 + bool isMainThread = true; 1.448 +#endif 1.449 + RegisterCurrentThread(isMainThread); 1.450 + sMasterList = new MasterList(); 1.451 + 1.452 + MainThreadIOLogger::Init(); 1.453 + 1.454 + // Now we initialize the various interposers depending on platform 1.455 + InitPoisonIOInterposer(); 1.456 + // We don't hook NSPR on Windows because PoisonIOInterposer captures a 1.457 + // superset of the former's events. 1.458 +#if !defined(XP_WIN) 1.459 + InitNSPRIOInterposing(); 1.460 +#endif 1.461 + return true; 1.462 +} 1.463 + 1.464 +bool 1.465 +IOInterposeObserver::IsMainThread() 1.466 +{ 1.467 + if (!sThreadLocalData.initialized()) { 1.468 + return false; 1.469 + } 1.470 + PerThreadData *ptd = sThreadLocalData.get(); 1.471 + if (!ptd) { 1.472 + return false; 1.473 + } 1.474 + return ptd->IsMainThread(); 1.475 +} 1.476 + 1.477 +void 1.478 +IOInterposer::Clear() 1.479 +{ 1.480 + sMasterList = nullptr; 1.481 +} 1.482 + 1.483 +void 1.484 +IOInterposer::Disable() 1.485 +{ 1.486 + if (!sMasterList) { 1.487 + return; 1.488 + } 1.489 + sMasterList->Disable(); 1.490 +} 1.491 + 1.492 +void 1.493 +IOInterposer::Report(IOInterposeObserver::Observation& aObservation) 1.494 +{ 1.495 + MOZ_ASSERT(sMasterList); 1.496 + if (!sMasterList) { 1.497 + return; 1.498 + } 1.499 + 1.500 + PerThreadData* ptd = sThreadLocalData.get(); 1.501 + if (!ptd) { 1.502 + // In this case the current thread is not registered with IOInterposer. 1.503 + // Alternatively we could take the slow path and just lock everything if 1.504 + // we're not registered. That could potentially perform poorly, though. 1.505 + return; 1.506 + } 1.507 + sMasterList->Update(*ptd); 1.508 + 1.509 + // Don't try to report if there's nobody listening. 1.510 + if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) { 1.511 + return; 1.512 + } 1.513 + 1.514 + ptd->CallObservers(aObservation); 1.515 +} 1.516 + 1.517 +bool 1.518 +IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp) 1.519 +{ 1.520 + return sMasterList && sMasterList->IsObservedOperation(aOp); 1.521 +} 1.522 + 1.523 +void 1.524 +IOInterposer::Register(IOInterposeObserver::Operation aOp, 1.525 + IOInterposeObserver* aObserver) 1.526 +{ 1.527 + MOZ_ASSERT(aObserver); 1.528 + if (!sMasterList || !aObserver) { 1.529 + return; 1.530 + } 1.531 + 1.532 + sMasterList->Register(aOp, aObserver); 1.533 +} 1.534 + 1.535 +void 1.536 +IOInterposer::Unregister(IOInterposeObserver::Operation aOp, 1.537 + IOInterposeObserver* aObserver) 1.538 +{ 1.539 + if (!sMasterList) { 1.540 + return; 1.541 + } 1.542 + 1.543 + sMasterList->Unregister(aOp, aObserver); 1.544 +} 1.545 + 1.546 +void 1.547 +IOInterposer::RegisterCurrentThread(bool aIsMainThread) 1.548 +{ 1.549 + if (!sThreadLocalData.initialized()) { 1.550 + return; 1.551 + } 1.552 + MOZ_ASSERT(!sThreadLocalData.get()); 1.553 + PerThreadData* curThreadData = new PerThreadData(aIsMainThread); 1.554 + sThreadLocalData.set(curThreadData); 1.555 +} 1.556 + 1.557 +void 1.558 +IOInterposer::UnregisterCurrentThread() 1.559 +{ 1.560 + if (!sThreadLocalData.initialized()) { 1.561 + return; 1.562 + } 1.563 + PerThreadData* curThreadData = sThreadLocalData.get(); 1.564 + MOZ_ASSERT(curThreadData); 1.565 + sThreadLocalData.set(nullptr); 1.566 + delete curThreadData; 1.567 +} 1.568 + 1.569 +void 1.570 +IOInterposer::EnteringNextStage() 1.571 +{ 1.572 + if (!sMasterList) { 1.573 + return; 1.574 + } 1.575 + NextStageObservation observation; 1.576 + Report(observation); 1.577 +} 1.578 +