michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "IOInterposer.h" michael@0: #include "NSPRInterposer.h" michael@0: michael@0: #include "prio.h" michael@0: #include "private/pprio.h" michael@0: michael@0: namespace { michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /* Original IO methods */ michael@0: PRCloseFN sCloseFn = nullptr; michael@0: PRReadFN sReadFn = nullptr; michael@0: PRWriteFN sWriteFn = nullptr; michael@0: PRFsyncFN sFSyncFn = nullptr; michael@0: PRFileInfoFN sFileInfoFn = nullptr; michael@0: PRFileInfo64FN sFileInfo64Fn = nullptr; michael@0: michael@0: /** michael@0: * RAII class for timing the duration of an NSPR I/O call and reporting the michael@0: * result to the IOInterposeObserver API. michael@0: */ michael@0: class NSPRIOAutoObservation : public IOInterposeObserver::Observation michael@0: { michael@0: public: michael@0: NSPRIOAutoObservation(IOInterposeObserver::Operation aOp) michael@0: : IOInterposeObserver::Observation(aOp, "NSPRIOInterposer") michael@0: { michael@0: } michael@0: michael@0: ~NSPRIOAutoObservation() michael@0: { michael@0: Report(); michael@0: } michael@0: }; michael@0: michael@0: PRStatus PR_CALLBACK interposedClose(PRFileDesc* aFd) michael@0: { michael@0: // If we don't have a valid original function pointer something is very wrong. michael@0: NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL"); michael@0: michael@0: NSPRIOAutoObservation timer(IOInterposeObserver::OpClose); michael@0: return sCloseFn(aFd); michael@0: } michael@0: michael@0: int32_t PR_CALLBACK interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt) michael@0: { michael@0: // If we don't have a valid original function pointer something is very wrong. michael@0: NS_ASSERTION(sReadFn, "NSPR IO Interposing: sReadFn is NULL"); michael@0: michael@0: NSPRIOAutoObservation timer(IOInterposeObserver::OpRead); michael@0: return sReadFn(aFd, aBuf, aAmt); michael@0: } michael@0: michael@0: int32_t PR_CALLBACK interposedWrite(PRFileDesc* aFd, const void* aBuf, michael@0: int32_t aAmt) michael@0: { michael@0: // If we don't have a valid original function pointer something is very wrong. michael@0: NS_ASSERTION(sWriteFn, "NSPR IO Interposing: sWriteFn is NULL"); michael@0: michael@0: NSPRIOAutoObservation timer(IOInterposeObserver::OpWrite); michael@0: return sWriteFn(aFd, aBuf, aAmt); michael@0: } michael@0: michael@0: PRStatus PR_CALLBACK interposedFSync(PRFileDesc* aFd) michael@0: { michael@0: // If we don't have a valid original function pointer something is very wrong. michael@0: NS_ASSERTION(sFSyncFn, "NSPR IO Interposing: sFSyncFn is NULL"); michael@0: michael@0: NSPRIOAutoObservation timer(IOInterposeObserver::OpFSync); michael@0: return sFSyncFn(aFd); michael@0: } michael@0: michael@0: PRStatus PR_CALLBACK interposedFileInfo(PRFileDesc *aFd, PRFileInfo *aInfo) michael@0: { michael@0: // If we don't have a valid original function pointer something is very wrong. michael@0: NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL"); michael@0: michael@0: NSPRIOAutoObservation timer(IOInterposeObserver::OpStat); michael@0: return sFileInfoFn(aFd, aInfo); michael@0: } michael@0: michael@0: PRStatus PR_CALLBACK interposedFileInfo64(PRFileDesc *aFd, PRFileInfo64 *aInfo) michael@0: { michael@0: // If we don't have a valid original function pointer something is very wrong. michael@0: NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL"); michael@0: michael@0: NSPRIOAutoObservation timer(IOInterposeObserver::OpStat); michael@0: return sFileInfo64Fn(aFd, aInfo); michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: namespace mozilla { michael@0: michael@0: void InitNSPRIOInterposing() michael@0: { michael@0: // Check that we have not interposed any of the IO methods before michael@0: MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn && michael@0: !sFileInfo64Fn); michael@0: michael@0: // We can't actually use this assertion because we initialize this code michael@0: // before XPCOM is initialized, so NS_IsMainThread() always returns false. michael@0: // MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Get IO methods from NSPR and const cast the structure so we can modify it. michael@0: PRIOMethods* methods = const_cast(PR_GetFileMethods()); michael@0: michael@0: // Something is badly wrong if we don't get IO methods... However, we don't michael@0: // want to crash over that in non-debug builds. This is unlikely to happen michael@0: // so an assert is enough, no need to report it to the caller. michael@0: MOZ_ASSERT(methods); michael@0: if (!methods) { michael@0: return; michael@0: } michael@0: michael@0: // Store original functions michael@0: sCloseFn = methods->close; michael@0: sReadFn = methods->read; michael@0: sWriteFn = methods->write; michael@0: sFSyncFn = methods->fsync; michael@0: sFileInfoFn = methods->fileInfo; michael@0: sFileInfo64Fn = methods->fileInfo64; michael@0: michael@0: // Overwrite with our interposed functions michael@0: methods->close = &interposedClose; michael@0: methods->read = &interposedRead; michael@0: methods->write = &interposedWrite; michael@0: methods->fsync = &interposedFSync; michael@0: methods->fileInfo = &interposedFileInfo; michael@0: methods->fileInfo64 = &interposedFileInfo64; michael@0: } michael@0: michael@0: void ClearNSPRIOInterposing() michael@0: { michael@0: // If we have already cleared IO interposing, or not initialized it this is michael@0: // actually bad. michael@0: MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn && michael@0: sFileInfo64Fn); michael@0: michael@0: // Get IO methods from NSPR and const cast the structure so we can modify it. michael@0: PRIOMethods* methods = const_cast(PR_GetFileMethods()); michael@0: michael@0: // Something is badly wrong if we don't get IO methods... However, we don't michael@0: // want to crash over that in non-debug builds. This is unlikely to happen michael@0: // so an assert is enough, no need to report it to the caller. michael@0: MOZ_ASSERT(methods); michael@0: if (!methods) { michael@0: return; michael@0: } michael@0: michael@0: // Restore original functions michael@0: methods->close = sCloseFn; michael@0: methods->read = sReadFn; michael@0: methods->write = sWriteFn; michael@0: methods->fsync = sFSyncFn; michael@0: methods->fileInfo = sFileInfoFn; michael@0: methods->fileInfo64 = sFileInfo64Fn; michael@0: michael@0: // Forget about original functions michael@0: sCloseFn = nullptr; michael@0: sReadFn = nullptr; michael@0: sWriteFn = nullptr; michael@0: sFSyncFn = nullptr; michael@0: sFileInfoFn = nullptr; michael@0: sFileInfo64Fn = nullptr; michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: