xpcom/build/IOInterposer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include <algorithm>
     6 #include <vector>
     8 #include "IOInterposer.h"
    10 #include "IOInterposerPrivate.h"
    11 #include "MainThreadIOLogger.h"
    12 #include "mozilla/Atomics.h"
    13 #include "mozilla/Mutex.h"
    14 #if defined(MOZILLA_INTERNAL_API)
    15 // We need to undefine MOZILLA_INTERNAL_API for RefPtr.h because IOInterposer
    16 // does not clean up its data before shutdown.
    17 #undef MOZILLA_INTERNAL_API
    18 #include "mozilla/RefPtr.h"
    19 #define MOZILLA_INTERNAL_API
    20 #else
    21 #include "mozilla/RefPtr.h"
    22 #endif // defined(MOZILLA_INTERNAL_API)
    23 #include "mozilla/StaticPtr.h"
    24 #include "mozilla/ThreadLocal.h"
    25 #if !defined(XP_WIN)
    26 #include "NSPRInterposer.h"
    27 #endif // !defined(XP_WIN)
    28 #include "nsXULAppAPI.h"
    29 #include "PoisonIOInterposer.h"
    31 using namespace mozilla;
    33 namespace {
    35 /** Find if a vector contains a specific element */
    36 template<class T>
    37 bool VectorContains(const std::vector<T>& vector, const T& element)
    38 {
    39   return std::find(vector.begin(), vector.end(), element) != vector.end();
    40 }
    42 /** Remove element from a vector */
    43 template<class T>
    44 void VectorRemove(std::vector<T>& vector, const T& element)
    45 {
    46   typename std::vector<T>::iterator newEnd = std::remove(vector.begin(),
    47                                                          vector.end(), element);
    48   vector.erase(newEnd, vector.end());
    49 }
    51 /** Lists of Observers */
    52 struct ObserverLists : public mozilla::AtomicRefCounted<ObserverLists>
    53 {
    54   ObserverLists()
    55   {
    56   }
    58   ObserverLists(ObserverLists const & aOther)
    59     : mCreateObservers(aOther.mCreateObservers)
    60     , mReadObservers(aOther.mReadObservers)
    61     , mWriteObservers(aOther.mWriteObservers)
    62     , mFSyncObservers(aOther.mFSyncObservers)
    63     , mStatObservers(aOther.mStatObservers)
    64     , mCloseObservers(aOther.mCloseObservers)
    65     , mStageObservers(aOther.mStageObservers)
    66   {
    67   }
    68   // Lists of observers for I/O events.
    69   // These are implemented as vectors since they are allowed to survive gecko,
    70   // without reporting leaks. This is necessary for the IOInterposer to be used
    71   // for late-write checks.
    72   std::vector<IOInterposeObserver*>  mCreateObservers;
    73   std::vector<IOInterposeObserver*>  mReadObservers;
    74   std::vector<IOInterposeObserver*>  mWriteObservers;
    75   std::vector<IOInterposeObserver*>  mFSyncObservers;
    76   std::vector<IOInterposeObserver*>  mStatObservers;
    77   std::vector<IOInterposeObserver*>  mCloseObservers;
    78   std::vector<IOInterposeObserver*>  mStageObservers;
    79 };
    81 class PerThreadData
    82 {
    83 public:
    84   PerThreadData(bool aIsMainThread = false)
    85     : mIsMainThread(aIsMainThread)
    86     , mIsHandlingObservation(false)
    87     , mCurrentGeneration(0)
    88   {
    89   }
    91   void
    92   CallObservers(IOInterposeObserver::Observation& aObservation)
    93   {
    94     // Prevent recursive reporting.
    95     if (mIsHandlingObservation) {
    96       return;
    97     }
    99     mIsHandlingObservation = true;
   100     // Decide which list of observers to inform
   101     std::vector<IOInterposeObserver*>* observers = nullptr;
   102     switch (aObservation.ObservedOperation()) {
   103       case IOInterposeObserver::OpCreateOrOpen:
   104         {
   105           observers = &mObserverLists->mCreateObservers;
   106         }
   107         break;
   108       case IOInterposeObserver::OpRead:
   109         {
   110           observers = &mObserverLists->mReadObservers;
   111         }
   112         break;
   113       case IOInterposeObserver::OpWrite:
   114         {
   115           observers = &mObserverLists->mWriteObservers;
   116         }
   117         break;
   118       case IOInterposeObserver::OpFSync:
   119         {
   120           observers = &mObserverLists->mFSyncObservers;
   121         }
   122         break;
   123       case IOInterposeObserver::OpStat:
   124         {
   125           observers = &mObserverLists->mStatObservers;
   126         }
   127         break;
   128       case IOInterposeObserver::OpClose:
   129         {
   130           observers = &mObserverLists->mCloseObservers;
   131         }
   132         break;
   133       case IOInterposeObserver::OpNextStage:
   134         {
   135           observers = &mObserverLists->mStageObservers;
   136         }
   137         break;
   138       default:
   139         {
   140           // Invalid IO operation, see documentation comment for
   141           // IOInterposer::Report()
   142           MOZ_ASSERT(false);
   143           // Just ignore it in non-debug builds.
   144           return;
   145         }
   146     }
   147     MOZ_ASSERT(observers);
   149     // Inform observers
   150     for (std::vector<IOInterposeObserver*>::iterator i = observers->begin(),
   151          e = observers->end(); i != e; ++i)
   152     {
   153       (*i)->Observe(aObservation);
   154     }
   155     mIsHandlingObservation = false;
   156   }
   158   inline uint32_t
   159   GetCurrentGeneration() const
   160   {
   161     return mCurrentGeneration;
   162   }
   164   inline bool
   165   IsMainThread() const
   166   {
   167     return mIsMainThread;
   168   }
   170   inline void
   171   SetObserverLists(uint32_t aNewGeneration, RefPtr<ObserverLists>& aNewLists)
   172   {
   173     mCurrentGeneration = aNewGeneration;
   174     mObserverLists = aNewLists;
   175   }
   177 private:
   178   bool                  mIsMainThread;
   179   bool                  mIsHandlingObservation;
   180   uint32_t              mCurrentGeneration;
   181   RefPtr<ObserverLists> mObserverLists;
   182 };
   184 class MasterList
   185 {
   186 public:
   187   MasterList()
   188     : mObservedOperations(IOInterposeObserver::OpNone)
   189     , mIsEnabled(true)
   190   {
   191   }
   193   ~MasterList()
   194   {
   195   }
   197   inline void
   198   Disable()
   199   {
   200     mIsEnabled = false;
   201   }
   203   void
   204   Register(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
   205   {
   206     IOInterposer::AutoLock lock(mLock);
   208     ObserverLists* newLists = nullptr;
   209     if (mObserverLists) {
   210       newLists = new ObserverLists(*mObserverLists);
   211     } else {
   212       newLists = new ObserverLists();
   213     }
   214     // You can register to observe multiple types of observations
   215     // but you'll never be registered twice for the same observations.
   216     if (aOp & IOInterposeObserver::OpCreateOrOpen &&
   217         !VectorContains(newLists->mCreateObservers, aObserver)) {
   218       newLists->mCreateObservers.push_back(aObserver);
   219     }
   220     if (aOp & IOInterposeObserver::OpRead &&
   221         !VectorContains(newLists->mReadObservers, aObserver)) {
   222       newLists->mReadObservers.push_back(aObserver);
   223     }
   224     if (aOp & IOInterposeObserver::OpWrite &&
   225         !VectorContains(newLists->mWriteObservers, aObserver)) {
   226       newLists->mWriteObservers.push_back(aObserver);
   227     }
   228     if (aOp & IOInterposeObserver::OpFSync &&
   229         !VectorContains(newLists->mFSyncObservers, aObserver)) {
   230       newLists->mFSyncObservers.push_back(aObserver);
   231     }
   232     if (aOp & IOInterposeObserver::OpStat &&
   233         !VectorContains(newLists->mStatObservers, aObserver)) {
   234       newLists->mStatObservers.push_back(aObserver);
   235     }
   236     if (aOp & IOInterposeObserver::OpClose &&
   237         !VectorContains(newLists->mCloseObservers, aObserver)) {
   238       newLists->mCloseObservers.push_back(aObserver);
   239     }
   240     if (aOp & IOInterposeObserver::OpNextStage &&
   241         !VectorContains(newLists->mStageObservers, aObserver)) {
   242       newLists->mStageObservers.push_back(aObserver);
   243     }
   244     mObserverLists = newLists;
   245     mObservedOperations = (IOInterposeObserver::Operation)
   246                             (mObservedOperations | aOp);
   248     mCurrentGeneration++;
   249   }
   251   void
   252   Unregister(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
   253   {
   254     IOInterposer::AutoLock lock(mLock);
   256     ObserverLists* newLists = nullptr;
   257     if (mObserverLists) {
   258       newLists = new ObserverLists(*mObserverLists);
   259     } else {
   260       newLists = new ObserverLists();
   261     }
   263     if (aOp & IOInterposeObserver::OpCreateOrOpen) {
   264       VectorRemove(newLists->mCreateObservers, aObserver);
   265       if (newLists->mCreateObservers.empty()) {
   266         mObservedOperations = (IOInterposeObserver::Operation)
   267                          (mObservedOperations &
   268                           ~IOInterposeObserver::OpCreateOrOpen);
   269       }
   270     }
   271     if (aOp & IOInterposeObserver::OpRead) {
   272       VectorRemove(newLists->mReadObservers, aObserver);
   273       if (newLists->mReadObservers.empty()) {
   274         mObservedOperations = (IOInterposeObserver::Operation)
   275                          (mObservedOperations & ~IOInterposeObserver::OpRead);
   276       }
   277     }
   278     if (aOp & IOInterposeObserver::OpWrite) {
   279       VectorRemove(newLists->mWriteObservers, aObserver);
   280       if (newLists->mWriteObservers.empty()) {
   281         mObservedOperations = (IOInterposeObserver::Operation)
   282                          (mObservedOperations & ~IOInterposeObserver::OpWrite);
   283       }
   284     }
   285     if (aOp & IOInterposeObserver::OpFSync) {
   286       VectorRemove(newLists->mFSyncObservers, aObserver);
   287       if (newLists->mFSyncObservers.empty()) {
   288         mObservedOperations = (IOInterposeObserver::Operation)
   289                          (mObservedOperations & ~IOInterposeObserver::OpFSync);
   290       }
   291     }
   292     if (aOp & IOInterposeObserver::OpStat) {
   293       VectorRemove(newLists->mStatObservers, aObserver);
   294       if (newLists->mStatObservers.empty()) {
   295         mObservedOperations = (IOInterposeObserver::Operation)
   296                          (mObservedOperations & ~IOInterposeObserver::OpStat);
   297       }
   298     }
   299     if (aOp & IOInterposeObserver::OpClose) {
   300       VectorRemove(newLists->mCloseObservers, aObserver);
   301       if (newLists->mCloseObservers.empty()) {
   302         mObservedOperations = (IOInterposeObserver::Operation)
   303                          (mObservedOperations & ~IOInterposeObserver::OpClose);
   304       }
   305     }
   306     if (aOp & IOInterposeObserver::OpNextStage) {
   307       VectorRemove(newLists->mStageObservers, aObserver);
   308       if (newLists->mStageObservers.empty()) {
   309         mObservedOperations = (IOInterposeObserver::Operation)
   310                          (mObservedOperations & ~IOInterposeObserver::OpNextStage);
   311       }
   312     }
   313     mObserverLists = newLists;
   314     mCurrentGeneration++;
   315   }
   317   void
   318   Update(PerThreadData &aPtd)
   319   {
   320     if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
   321       return;
   322     }
   323     // If the generation counts don't match then we need to update the current
   324     // thread's observer list with the new master list.
   325     IOInterposer::AutoLock lock(mLock);
   326     aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
   327   }
   329   inline bool
   330   IsObservedOperation(IOInterposeObserver::Operation aOp)
   331   {
   332     // The quick reader may observe that no locks are being employed here,
   333     // hence the result of the operations is truly undefined. However, most
   334     // computers will usually return either true or false, which is good enough.
   335     // It is not a problem if we occasionally report more or less IO than is
   336     // actually occurring.
   337     return mIsEnabled && !!(mObservedOperations & aOp);
   338   }
   340 private:
   341   RefPtr<ObserverLists>             mObserverLists;
   342   // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked
   343   // (We want to monitor IO during shutdown). Furthermore, as we may have to
   344   // unregister observers during shutdown an OffTheBooksMutex is not an option
   345   // either, as its base calls into sDeadlockDetector which may be nullptr
   346   // during shutdown.
   347   IOInterposer::Mutex               mLock;
   348   // Flags tracking which operations are being observed
   349   IOInterposeObserver::Operation    mObservedOperations;
   350   // Used for quickly disabling everything by IOInterposer::Disable()
   351   Atomic<bool>                      mIsEnabled;
   352   // Used to inform threads that the master observer list has changed
   353   Atomic<uint32_t>                  mCurrentGeneration;
   354 };
   356 // Special observation used by IOInterposer::EnteringNextStage()
   357 class NextStageObservation : public IOInterposeObserver::Observation
   358 {
   359 public:
   360   NextStageObservation()
   361     : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
   362                                        "IOInterposer", false)
   363   {
   364     mStart = TimeStamp::Now();
   365   }
   366 };
   368 // List of observers registered
   369 static StaticAutoPtr<MasterList> sMasterList;
   370 static ThreadLocal<PerThreadData*> sThreadLocalData;
   371 } // anonymous namespace
   373 IOInterposeObserver::Observation::Observation(Operation aOperation,
   374                                               const char* aReference,
   375                                               bool aShouldReport)
   376   : mOperation(aOperation)
   377   , mReference(aReference)
   378   , mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
   379                   aShouldReport)
   380 {
   381   if (mShouldReport) {
   382     mStart = TimeStamp::Now();
   383   }
   384 }
   386 IOInterposeObserver::Observation::Observation(Operation aOperation,
   387                                               const TimeStamp& aStart,
   388                                               const TimeStamp& aEnd,
   389                                               const char* aReference)
   390   : mOperation(aOperation)
   391   , mStart(aStart)
   392   , mEnd(aEnd)
   393   , mReference(aReference)
   394   , mShouldReport(false)
   395 {
   396 }
   398 const char*
   399 IOInterposeObserver::Observation::ObservedOperationString() const
   400 {
   401   switch(mOperation) {
   402     case OpCreateOrOpen:
   403       return "create/open";
   404     case OpRead:
   405       return "read";
   406     case OpWrite:
   407       return "write";
   408     case OpFSync:
   409       return "fsync";
   410     case OpStat:
   411       return "stat";
   412     case OpClose:
   413       return "close";
   414     case OpNextStage:
   415       return "NextStage";
   416     default:
   417       return "unknown";
   418   }
   419 }
   421 void
   422 IOInterposeObserver::Observation::Report()
   423 {
   424   if (mShouldReport) {
   425     mEnd = TimeStamp::Now();
   426     IOInterposer::Report(*this);
   427   }
   428 }
   430 bool
   431 IOInterposer::Init()
   432 {
   433   // Don't initialize twice...
   434   if (sMasterList) {
   435     return true;
   436   }
   437   if (!sThreadLocalData.init()) {
   438     return false;
   439   }
   440 #if defined(XP_WIN)
   441   bool isMainThread = XRE_GetWindowsEnvironment() !=
   442                         WindowsEnvironmentType_Metro;
   443 #else
   444   bool isMainThread = true;
   445 #endif
   446   RegisterCurrentThread(isMainThread);
   447   sMasterList = new MasterList();
   449   MainThreadIOLogger::Init();
   451   // Now we initialize the various interposers depending on platform
   452   InitPoisonIOInterposer();
   453   // We don't hook NSPR on Windows because PoisonIOInterposer captures a
   454   // superset of the former's events.
   455 #if !defined(XP_WIN)
   456   InitNSPRIOInterposing();
   457 #endif
   458   return true;
   459 }
   461 bool
   462 IOInterposeObserver::IsMainThread()
   463 {
   464   if (!sThreadLocalData.initialized()) {
   465     return false;
   466   }
   467   PerThreadData *ptd = sThreadLocalData.get();
   468   if (!ptd) {
   469     return false;
   470   }
   471   return ptd->IsMainThread();
   472 }
   474 void
   475 IOInterposer::Clear()
   476 {
   477   sMasterList = nullptr;
   478 }
   480 void
   481 IOInterposer::Disable()
   482 {
   483   if (!sMasterList) {
   484     return;
   485   }
   486   sMasterList->Disable();
   487 }
   489 void
   490 IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
   491 {
   492   MOZ_ASSERT(sMasterList);
   493   if (!sMasterList) {
   494     return;
   495   }
   497   PerThreadData* ptd = sThreadLocalData.get();
   498   if (!ptd) {
   499     // In this case the current thread is not registered with IOInterposer.
   500     // Alternatively we could take the slow path and just lock everything if
   501     // we're not registered. That could potentially perform poorly, though.
   502     return;
   503   }
   504   sMasterList->Update(*ptd);
   506   // Don't try to report if there's nobody listening.
   507   if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
   508     return;
   509   }
   511   ptd->CallObservers(aObservation);
   512 }
   514 bool
   515 IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
   516 {
   517   return sMasterList && sMasterList->IsObservedOperation(aOp);
   518 }
   520 void
   521 IOInterposer::Register(IOInterposeObserver::Operation aOp,
   522                        IOInterposeObserver* aObserver)
   523 {
   524   MOZ_ASSERT(aObserver);
   525   if (!sMasterList || !aObserver) {
   526     return;
   527   }
   529   sMasterList->Register(aOp, aObserver);
   530 }
   532 void
   533 IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
   534                          IOInterposeObserver* aObserver)
   535 {
   536   if (!sMasterList) {
   537     return;
   538   }
   540   sMasterList->Unregister(aOp, aObserver);
   541 }
   543 void
   544 IOInterposer::RegisterCurrentThread(bool aIsMainThread)
   545 {
   546   if (!sThreadLocalData.initialized()) {
   547     return;
   548   }
   549   MOZ_ASSERT(!sThreadLocalData.get());
   550   PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
   551   sThreadLocalData.set(curThreadData);
   552 }
   554 void
   555 IOInterposer::UnregisterCurrentThread()
   556 {
   557   if (!sThreadLocalData.initialized()) {
   558     return;
   559   }
   560   PerThreadData* curThreadData = sThreadLocalData.get();
   561   MOZ_ASSERT(curThreadData);
   562   sThreadLocalData.set(nullptr);
   563   delete curThreadData;
   564 }
   566 void
   567 IOInterposer::EnteringNextStage()
   568 {
   569   if (!sMasterList) {
   570     return;
   571   }
   572   NextStageObservation observation;
   573   Report(observation);
   574 }

mercurial