michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 ci et: */ 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 "mozilla/Mutex.h" michael@0: #include "mozilla/Scoped.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "PoisonIOInterposer.h" michael@0: michael@0: // Auxiliary method to convert file descriptors to ids michael@0: #if defined(XP_WIN32) michael@0: #include michael@0: inline intptr_t FileDescriptorToID(int aFd) { michael@0: return _get_osfhandle(aFd); michael@0: } michael@0: #else michael@0: inline intptr_t FileDescriptorToID(int aFd) { michael@0: return aFd; michael@0: } michael@0: #endif /* if not XP_WIN32 */ michael@0: michael@0: using namespace mozilla; michael@0: michael@0: namespace { michael@0: struct DebugFilesAutoLockTraits { michael@0: typedef PRLock *type; michael@0: const static type empty() { michael@0: return nullptr; michael@0: } michael@0: const static void release(type aL) { michael@0: PR_Unlock(aL); michael@0: } michael@0: }; michael@0: michael@0: class DebugFilesAutoLock : public Scoped { michael@0: static PRLock *Lock; michael@0: public: michael@0: static void Clear(); michael@0: static PRLock *getDebugFileIDsLock() { michael@0: // On windows this static is not thread safe, but we know that the first michael@0: // call is from michael@0: // * An early registration of a debug FD or michael@0: // * The call to InitWritePoisoning. michael@0: // Since the early debug FDs are logs created early in the main thread michael@0: // and no writes are trapped before InitWritePoisoning, we are safe. michael@0: if (!Lock) { michael@0: Lock = PR_NewLock(); michael@0: } michael@0: michael@0: // We have to use something lower level than a mutex. If we don't, we michael@0: // can get recursive in here when called from logging a call to free. michael@0: return Lock; michael@0: } michael@0: michael@0: DebugFilesAutoLock() : michael@0: Scoped(getDebugFileIDsLock()) { michael@0: PR_Lock(get()); michael@0: } michael@0: }; michael@0: michael@0: PRLock *DebugFilesAutoLock::Lock; michael@0: void DebugFilesAutoLock::Clear() { michael@0: MOZ_ASSERT(Lock != nullptr); michael@0: Lock = nullptr; michael@0: } michael@0: michael@0: // Return a vector used to hold the IDs of the current debug files. On unix michael@0: // an ID is a file descriptor. On Windows it is a file HANDLE. michael@0: std::vector* getDebugFileIDs() { michael@0: PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(DebugFilesAutoLock::getDebugFileIDsLock()); michael@0: // We have to use new as some write happen during static destructors michael@0: // so an static std::vector might be destroyed while we still need it. michael@0: static std::vector *DebugFileIDs = new std::vector(); michael@0: return DebugFileIDs; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: namespace mozilla{ michael@0: michael@0: // Auxiliary Method to test if a file descriptor is registered to be ignored michael@0: // by the poisoning IO interposer michael@0: bool IsDebugFile(intptr_t aFileID) { michael@0: DebugFilesAutoLock lockedScope; michael@0: michael@0: std::vector &Vec = *getDebugFileIDs(); michael@0: return std::find(Vec.begin(), Vec.end(), aFileID) != Vec.end(); michael@0: } michael@0: michael@0: // Clean-up for the registered debug files. michael@0: // We should probably make sure all debug files are unregistered instead. michael@0: // But as the poison IO interposer is used for late-write checks we're not michael@0: // disabling it at any point yet. So Really no need for this. michael@0: // michael@0: // void ClearDebugFileRegister() { michael@0: // PRLock *Lock; michael@0: // { michael@0: // DebugFilesAutoLock lockedScope; michael@0: // delete getDebugFileIDs(); michael@0: // Lock = DebugFilesAutoLock::getDebugFileIDsLock(); michael@0: // DebugFilesAutoLock::Clear(); michael@0: // } michael@0: // PR_DestroyLock(Lock); michael@0: // } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: extern "C" { michael@0: michael@0: void MozillaRegisterDebugFD(int fd) { michael@0: intptr_t fileId = FileDescriptorToID(fd); michael@0: DebugFilesAutoLock lockedScope; michael@0: std::vector &Vec = *getDebugFileIDs(); michael@0: MOZ_ASSERT(std::find(Vec.begin(), Vec.end(), fileId) == Vec.end()); michael@0: Vec.push_back(fileId); michael@0: } michael@0: michael@0: void MozillaRegisterDebugFILE(FILE *f) { michael@0: int fd = fileno(f); michael@0: if (fd == 1 || fd == 2) { michael@0: return; michael@0: } michael@0: MozillaRegisterDebugFD(fd); michael@0: } michael@0: michael@0: void MozillaUnRegisterDebugFD(int fd) { michael@0: DebugFilesAutoLock lockedScope; michael@0: intptr_t fileId = FileDescriptorToID(fd); michael@0: std::vector &Vec = *getDebugFileIDs(); michael@0: std::vector::iterator i = michael@0: std::find(Vec.begin(), Vec.end(), fileId); michael@0: MOZ_ASSERT(i != Vec.end()); michael@0: Vec.erase(i); michael@0: } michael@0: michael@0: void MozillaUnRegisterDebugFILE(FILE *f) { michael@0: int fd = fileno(f); michael@0: if (fd == 1 || fd == 2) { michael@0: return; michael@0: } michael@0: fflush(f); michael@0: MozillaUnRegisterDebugFD(fd); michael@0: } michael@0: michael@0: }