diff options
Diffstat (limited to 'xpcom/build')
43 files changed, 10686 insertions, 0 deletions
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h new file mode 100644 index 000000000..374763c79 --- /dev/null +++ b/xpcom/build/BinaryPath.h @@ -0,0 +1,188 @@ +/* -*- 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/. */ + +#ifndef mozilla_BinaryPath_h +#define mozilla_BinaryPath_h + +#include "nsXPCOMPrivate.h" // for MAXPATHLEN +#ifdef XP_WIN +#include <windows.h> +#elif defined(XP_MACOSX) +#include <CoreFoundation/CoreFoundation.h> +#elif defined(XP_UNIX) +#include <sys/stat.h> +#include <string.h> +#endif + +namespace mozilla { + +class BinaryPath +{ +public: +#ifdef XP_WIN + static nsresult Get(const char* argv0, char aResult[MAXPATHLEN]) + { + wchar_t wide_path[MAXPATHLEN]; + nsresult rv = GetW(argv0, wide_path); + if (NS_FAILED(rv)) { + return rv; + } + WideCharToMultiByte(CP_UTF8, 0, wide_path, -1, + aResult, MAXPATHLEN, nullptr, nullptr); + return NS_OK; + } + +private: + static nsresult GetW(const char* argv0, wchar_t aResult[MAXPATHLEN]) + { + if (::GetModuleFileNameW(0, aResult, MAXPATHLEN)) { + return NS_OK; + } + return NS_ERROR_FAILURE; + } + +#elif defined(XP_MACOSX) + static nsresult Get(const char* argv0, char aResult[MAXPATHLEN]) + { + // Works even if we're not bundled. + CFBundleRef appBundle = CFBundleGetMainBundle(); + if (!appBundle) { + return NS_ERROR_FAILURE; + } + + CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle); + if (!executableURL) { + return NS_ERROR_FAILURE; + } + + nsresult rv; + if (CFURLGetFileSystemRepresentation(executableURL, false, (UInt8*)aResult, + MAXPATHLEN)) { + // Sanitize path in case the app was launched from Terminal via + // './firefox' for example. + size_t readPos = 0; + size_t writePos = 0; + while (aResult[readPos] != '\0') { + if (aResult[readPos] == '.' && aResult[readPos + 1] == '/') { + readPos += 2; + } else { + aResult[writePos] = aResult[readPos]; + readPos++; + writePos++; + } + } + aResult[writePos] = '\0'; + rv = NS_OK; + } else { + rv = NS_ERROR_FAILURE; + } + + CFRelease(executableURL); + return rv; + } + +#elif defined(ANDROID) + static nsresult Get(const char* argv0, char aResult[MAXPATHLEN]) + { + // On Android, we use the GRE_HOME variable that is set by the Java + // bootstrap code. + const char* greHome = getenv("GRE_HOME"); +#if defined(MOZ_WIDGET_GONK) + if (!greHome) { + greHome = "/system/b2g"; + } +#endif + + if (!greHome) { + return NS_ERROR_FAILURE; + } + + snprintf(aResult, MAXPATHLEN, "%s/%s", greHome, "dummy"); + aResult[MAXPATHLEN - 1] = '\0'; + return NS_OK; + } + +#elif defined(XP_UNIX) + static nsresult Get(const char* aArgv0, char aResult[MAXPATHLEN]) + { + struct stat fileStat; + // on unix, there is no official way to get the path of the current binary. + // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to + // multiple applications, we will try a series of techniques: + // + // 1) use realpath() on argv[0], which works unless we're loaded from the + // PATH. Only do so if argv[0] looks like a path (contains a /). + // 2) manually walk through the PATH and look for ourself + // 3) give up + if (strchr(aArgv0, '/') && realpath(aArgv0, aResult) && + stat(aResult, &fileStat) == 0) { + return NS_OK; + } + + const char* path = getenv("PATH"); + if (!path) { + return NS_ERROR_FAILURE; + } + + char* pathdup = strdup(path); + if (!pathdup) { + return NS_ERROR_OUT_OF_MEMORY; + } + + bool found = false; + char* token = strtok(pathdup, ":"); + while (token) { + char tmpPath[MAXPATHLEN]; + sprintf(tmpPath, "%s/%s", token, aArgv0); + if (realpath(tmpPath, aResult) && stat(aResult, &fileStat) == 0) { + found = true; + break; + } + token = strtok(nullptr, ":"); + } + free(pathdup); + if (found) { + return NS_OK; + } + return NS_ERROR_FAILURE; + } + +#else +#error Oops, you need platform-specific code here +#endif + +public: + static nsresult GetFile(const char* aArgv0, nsIFile** aResult) + { + nsCOMPtr<nsIFile> lf; +#ifdef XP_WIN + wchar_t exePath[MAXPATHLEN]; + nsresult rv = GetW(aArgv0, exePath); +#else + char exePath[MAXPATHLEN]; + nsresult rv = Get(aArgv0, exePath); +#endif + if (NS_FAILED(rv)) { + return rv; + } +#ifdef XP_WIN + rv = NS_NewLocalFile(nsDependentString(exePath), true, + getter_AddRefs(lf)); +#else + rv = NS_NewNativeLocalFile(nsDependentCString(exePath), true, + getter_AddRefs(lf)); +#endif + if (NS_FAILED(rv)) { + return rv; + } + NS_ADDREF(*aResult = lf); + return NS_OK; + } +}; + +} // namespace mozilla + +#endif /* mozilla_BinaryPath_h */ diff --git a/xpcom/build/FileLocation.cpp b/xpcom/build/FileLocation.cpp new file mode 100644 index 000000000..03c1dc027 --- /dev/null +++ b/xpcom/build/FileLocation.cpp @@ -0,0 +1,224 @@ +/* -*- 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 "FileLocation.h" +#include "nsZipArchive.h" +#include "nsURLHelper.h" + +namespace mozilla { + +FileLocation::FileLocation() +{ +} + +FileLocation::~FileLocation() +{ +} + +FileLocation::FileLocation(nsIFile* aFile) +{ + Init(aFile); +} + +FileLocation::FileLocation(nsIFile* aFile, const char* aPath) +{ + Init(aFile, aPath); +} + +FileLocation::FileLocation(const FileLocation& aFile, const char* aPath) +{ + if (aFile.IsZip()) { + if (aFile.mBaseFile) { + Init(aFile.mBaseFile, aFile.mPath.get()); + } + else { + Init(aFile.mBaseZip, aFile.mPath.get()); + } + if (aPath) { + int32_t i = mPath.RFindChar('/'); + if (kNotFound == i) { + mPath.Truncate(0); + } else { + mPath.Truncate(i + 1); + } + mPath += aPath; + } + } else { + if (aPath) { + nsCOMPtr<nsIFile> cfile; + aFile.mBaseFile->GetParent(getter_AddRefs(cfile)); + +#if defined(XP_WIN) + nsAutoCString pathStr(aPath); + char* p; + uint32_t len = pathStr.GetMutableData(&p); + for (; len; ++p, --len) { + if ('/' == *p) { + *p = '\\'; + } + } + cfile->AppendRelativeNativePath(pathStr); +#else + cfile->AppendRelativeNativePath(nsDependentCString(aPath)); +#endif + Init(cfile); + } else { + Init(aFile.mBaseFile); + } + } +} + +void +FileLocation::Init(nsIFile* aFile) +{ + mBaseZip = nullptr; + mBaseFile = aFile; + mPath.Truncate(); +} + +void +FileLocation::Init(nsIFile* aFile, const char* aPath) +{ + mBaseZip = nullptr; + mBaseFile = aFile; + mPath = aPath; +} + +void +FileLocation::Init(nsZipArchive* aZip, const char* aPath) +{ + mBaseZip = aZip; + mBaseFile = nullptr; + mPath = aPath; +} + +void +FileLocation::GetURIString(nsACString& aResult) const +{ + if (mBaseFile) { + net_GetURLSpecFromActualFile(mBaseFile, aResult); + } else if (mBaseZip) { + RefPtr<nsZipHandle> handler = mBaseZip->GetFD(); + handler->mFile.GetURIString(aResult); + } + if (IsZip()) { + aResult.Insert("jar:", 0); + aResult += "!/"; + aResult += mPath; + } +} + +already_AddRefed<nsIFile> +FileLocation::GetBaseFile() +{ + if (IsZip() && mBaseZip) { + RefPtr<nsZipHandle> handler = mBaseZip->GetFD(); + if (handler) { + return handler->mFile.GetBaseFile(); + } + return nullptr; + } + + nsCOMPtr<nsIFile> file = mBaseFile; + return file.forget(); +} + +bool +FileLocation::Equals(const FileLocation& aFile) const +{ + if (mPath != aFile.mPath) { + return false; + } + + if (mBaseFile && aFile.mBaseFile) { + bool eq; + return NS_SUCCEEDED(mBaseFile->Equals(aFile.mBaseFile, &eq)) && eq; + } + + const FileLocation* a = this; + const FileLocation* b = &aFile; + if (a->mBaseZip) { + RefPtr<nsZipHandle> handler = a->mBaseZip->GetFD(); + a = &handler->mFile; + } + if (b->mBaseZip) { + RefPtr<nsZipHandle> handler = b->mBaseZip->GetFD(); + b = &handler->mFile; + } + + return a->Equals(*b); +} + +nsresult +FileLocation::GetData(Data& aData) +{ + if (!IsZip()) { + return mBaseFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &aData.mFd.rwget()); + } + aData.mZip = mBaseZip; + if (!aData.mZip) { + aData.mZip = new nsZipArchive(); + aData.mZip->OpenArchive(mBaseFile); + } + aData.mItem = aData.mZip->GetItem(mPath.get()); + if (aData.mItem) { + return NS_OK; + } + return NS_ERROR_FILE_UNRECOGNIZED_PATH; +} + +nsresult +FileLocation::Data::GetSize(uint32_t* aResult) +{ + if (mFd) { + PRFileInfo64 fileInfo; + if (PR_SUCCESS != PR_GetOpenFileInfo64(mFd, &fileInfo)) { + return NS_ErrorAccordingToNSPR(); + } + + if (fileInfo.size > int64_t(UINT32_MAX)) { + return NS_ERROR_FILE_TOO_BIG; + } + + *aResult = fileInfo.size; + return NS_OK; + } + else if (mItem) { + *aResult = mItem->RealSize(); + return NS_OK; + } + return NS_ERROR_NOT_INITIALIZED; +} + +nsresult +FileLocation::Data::Copy(char* aBuf, uint32_t aLen) +{ + if (mFd) { + for (uint32_t totalRead = 0; totalRead < aLen;) { + int32_t read = PR_Read(mFd, aBuf + totalRead, + XPCOM_MIN(aLen - totalRead, uint32_t(INT32_MAX))); + if (read < 0) { + return NS_ErrorAccordingToNSPR(); + } + totalRead += read; + } + return NS_OK; + } + else if (mItem) { + nsZipCursor cursor(mItem, mZip, reinterpret_cast<uint8_t*>(aBuf), + aLen, true); + uint32_t readLen; + cursor.Copy(&readLen); + if (readLen != aLen) { + nsZipArchive::sFileCorruptedReason = "FileLocation::Data: insufficient data"; + return NS_ERROR_FILE_CORRUPTED; + } + return NS_OK; + } + return NS_ERROR_NOT_INITIALIZED; +} + +} /* namespace mozilla */ diff --git a/xpcom/build/FileLocation.h b/xpcom/build/FileLocation.h new file mode 100644 index 000000000..e9a3fc5d0 --- /dev/null +++ b/xpcom/build/FileLocation.h @@ -0,0 +1,134 @@ +/* -*- 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/. */ + +#ifndef mozilla_FileLocation_h +#define mozilla_FileLocation_h + +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsIFile.h" +#include "FileUtils.h" + +class nsZipArchive; +class nsZipItem; + +namespace mozilla { + +class FileLocation +{ +public: + /** + * FileLocation is an helper to handle different kind of file locations + * within Gecko: + * - on filesystems + * - in archives + * - in archives within archives + * As such, it stores a path within an archive, as well as the archive + * path itself, or the complete file path alone when on a filesystem. + * When the archive is in an archive, an nsZipArchive is stored instead + * of a file path. + */ + FileLocation(); + ~FileLocation(); + + /** + * Constructor for plain files + */ + explicit FileLocation(nsIFile* aFile); + + /** + * Constructors for path within an archive. The archive can be given either + * as nsIFile or nsZipArchive. + */ + FileLocation(nsIFile* aZip, const char* aPath); + + FileLocation(nsZipArchive* aZip, const char* aPath); + + /** + * Creates a new file location relative to another one. + */ + FileLocation(const FileLocation& aFile, const char* aPath = nullptr); + + /** + * Initialization functions corresponding to constructors + */ + void Init(nsIFile* aFile); + + void Init(nsIFile* aZip, const char* aPath); + + void Init(nsZipArchive* aZip, const char* aPath); + + /** + * Returns an URI string corresponding to the file location + */ + void GetURIString(nsACString& aResult) const; + + /** + * Returns the base file of the location, where base file is defined as: + * - The file itself when the location is on a filesystem + * - The archive file when the location is in an archive + * - The outer archive file when the location is in an archive in an archive + */ + already_AddRefed<nsIFile> GetBaseFile(); + + /** + * Returns whether the "base file" (see GetBaseFile) is an archive + */ + bool IsZip() const { return !mPath.IsEmpty(); } + + /** + * Returns the path within the archive, when within an archive + */ + void GetPath(nsACString& aResult) const { aResult = mPath; } + + /** + * Boolean value corresponding to whether the file location is initialized + * or not. + */ + explicit operator bool() const { return mBaseFile || mBaseZip; } + + /** + * Returns whether another FileLocation points to the same resource + */ + bool Equals(const FileLocation& aFile) const; + + /** + * Data associated with a FileLocation. + */ + class Data + { + public: + /** + * Returns the data size + */ + nsresult GetSize(uint32_t* aResult); + + /** + * Copies the data in the given buffer + */ + nsresult Copy(char* aBuf, uint32_t aLen); + protected: + friend class FileLocation; + nsZipItem* mItem; + RefPtr<nsZipArchive> mZip; + mozilla::AutoFDClose mFd; + }; + + /** + * Returns the data associated with the resource pointed at by the file + * location. + */ + nsresult GetData(Data& aData); +private: + nsCOMPtr<nsIFile> mBaseFile; + RefPtr<nsZipArchive> mBaseZip; + nsCString mPath; +}; /* class FileLocation */ + +} /* namespace mozilla */ + +#endif /* mozilla_FileLocation_h */ diff --git a/xpcom/build/FrozenFunctions.cpp b/xpcom/build/FrozenFunctions.cpp new file mode 100644 index 000000000..8a957ca45 --- /dev/null +++ b/xpcom/build/FrozenFunctions.cpp @@ -0,0 +1,138 @@ +/* -*- 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 "nsXPCOM.h" +#include "nsXPCOMPrivate.h" +#include "nsXPCOMStrings.h" +#include "xptcall.h" + +#include <string.h> + +/** + * Private Method to register an exit routine. This method + * used to allow you to setup a callback that will be called from + * the NS_ShutdownXPCOM function after all services and + * components have gone away. It was fatally flawed in that the component + * DLL could be released before the exit function was called; it is now a + * stub implementation that does nothing. + */ +XPCOM_API(nsresult) +NS_RegisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine, uint32_t aPriority); + +XPCOM_API(nsresult) +NS_UnregisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine); + +static const XPCOMFunctions kFrozenFunctions = { + XPCOM_GLUE_VERSION, + sizeof(XPCOMFunctions), + &NS_InitXPCOM2, + &NS_ShutdownXPCOM, + &NS_GetServiceManager, + &NS_GetComponentManager, + &NS_GetComponentRegistrar, + &NS_GetMemoryManager, + &NS_NewLocalFile, + &NS_NewNativeLocalFile, + &NS_RegisterXPCOMExitRoutine, + &NS_UnregisterXPCOMExitRoutine, + + // these functions were added post 1.4 + &NS_GetDebug, + nullptr, + + // these functions were added post 1.6 + &NS_StringContainerInit, + &NS_StringContainerFinish, + &NS_StringGetData, + &NS_StringSetData, + &NS_StringSetDataRange, + &NS_StringCopy, + &NS_CStringContainerInit, + &NS_CStringContainerFinish, + &NS_CStringGetData, + &NS_CStringSetData, + &NS_CStringSetDataRange, + &NS_CStringCopy, + &NS_CStringToUTF16, + &NS_UTF16ToCString, + &NS_StringCloneData, + &NS_CStringCloneData, + + // these functions were added post 1.7 (post Firefox 1.0) + &moz_xmalloc, + &moz_xrealloc, + &free, + &NS_StringContainerInit2, + &NS_CStringContainerInit2, + &NS_StringGetMutableData, + &NS_CStringGetMutableData, + nullptr, + + // these functions were added post 1.8 + &NS_DebugBreak, + &NS_LogInit, + &NS_LogTerm, + &NS_LogAddRef, + &NS_LogRelease, + &NS_LogCtor, + &NS_LogDtor, + &NS_LogCOMPtrAddRef, + &NS_LogCOMPtrRelease, + &NS_GetXPTCallStub, + &NS_DestroyXPTCallStub, + &NS_InvokeByIndex, + nullptr, + nullptr, + &NS_StringSetIsVoid, + &NS_StringGetIsVoid, + &NS_CStringSetIsVoid, + &NS_CStringGetIsVoid, + + // these functions were added post 1.9, but then made obsolete + nullptr, + nullptr, + + &NS_CycleCollectorSuspect3, +}; + +EXPORT_XPCOM_API(nsresult) +NS_GetFrozenFunctions(XPCOMFunctions* aFunctions, const char* /* aLibraryPath */) +{ + if (!aFunctions) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (aFunctions->version != XPCOM_GLUE_VERSION) { + return NS_ERROR_FAILURE; + } + + uint32_t size = aFunctions->size; + if (size > sizeof(XPCOMFunctions)) { + size = sizeof(XPCOMFunctions); + } + + size -= offsetof(XPCOMFunctions, init); + + memcpy(&aFunctions->init, &kFrozenFunctions.init, size); + + return NS_OK; +} + +/* + * Stubs for nsXPCOMPrivate.h + */ + +EXPORT_XPCOM_API(nsresult) +NS_RegisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine, uint32_t aPriority) +{ + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +NS_UnregisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine) +{ + return NS_OK; +} diff --git a/xpcom/build/IOInterposer.cpp b/xpcom/build/IOInterposer.cpp new file mode 100644 index 000000000..1c3ff54d5 --- /dev/null +++ b/xpcom/build/IOInterposer.cpp @@ -0,0 +1,582 @@ +/* -*- 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" + +using namespace mozilla; + +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()); +} + +/** Lists of Observers */ +struct ObserverLists +{ +private: + ~ObserverLists() {} + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists) + + ObserverLists() {} + + 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<IOInterposeObserver*> mCreateObservers; + std::vector<IOInterposeObserver*> mReadObservers; + std::vector<IOInterposeObserver*> mWriteObservers; + std::vector<IOInterposeObserver*> mFSyncObservers; + std::vector<IOInterposeObserver*> mStatObservers; + std::vector<IOInterposeObserver*> mCloseObservers; + std::vector<IOInterposeObserver*> mStageObservers; +}; + +class PerThreadData +{ +public: + explicit PerThreadData(bool aIsMainThread = false) + : mIsMainThread(aIsMainThread) + , mIsHandlingObservation(false) + , mCurrentGeneration(0) + { + MOZ_COUNT_CTOR(PerThreadData); + } + + ~PerThreadData() + { + MOZ_COUNT_DTOR(PerThreadData); + } + + void CallObservers(IOInterposeObserver::Observation& aObservation) + { + // Prevent recursive reporting. + if (mIsHandlingObservation) { + return; + } + + mIsHandlingObservation = true; + // Decide which list of observers to inform + std::vector<IOInterposeObserver*>* observers = nullptr; + switch (aObservation.ObservedOperation()) { + case IOInterposeObserver::OpCreateOrOpen: + observers = &mObserverLists->mCreateObservers; + break; + case IOInterposeObserver::OpRead: + observers = &mObserverLists->mReadObservers; + break; + case IOInterposeObserver::OpWrite: + observers = &mObserverLists->mWriteObservers; + break; + case IOInterposeObserver::OpFSync: + observers = &mObserverLists->mFSyncObservers; + break; + case IOInterposeObserver::OpStat: + observers = &mObserverLists->mStatObservers; + break; + case IOInterposeObserver::OpClose: + observers = &mObserverLists->mCloseObservers; + break; + case IOInterposeObserver::OpNextStage: + observers = &mObserverLists->mStageObservers; + break; + default: { + // Invalid IO operation, see documentation comment for + // IOInterposer::Report() + MOZ_ASSERT(false); + // Just ignore it in non-debug builds. + return; + } + } + MOZ_ASSERT(observers); + + // Inform observers + for (auto i = observers->begin(), e = observers->end(); i != e; ++i) { + (*i)->Observe(aObservation); + } + mIsHandlingObservation = false; + } + + inline uint32_t GetCurrentGeneration() const { return mCurrentGeneration; } + + inline bool IsMainThread() const { return mIsMainThread; } + + inline void SetObserverLists(uint32_t aNewGeneration, + RefPtr<ObserverLists>& aNewLists) + { + mCurrentGeneration = aNewGeneration; + mObserverLists = aNewLists; + } + + inline void ClearObserverLists() + { + if (mObserverLists) { + mCurrentGeneration = 0; + mObserverLists = nullptr; + } + } + +private: + bool mIsMainThread; + bool mIsHandlingObservation; + uint32_t mCurrentGeneration; + RefPtr<ObserverLists> mObserverLists; +}; + +class MasterList +{ +public: + MasterList() + : mObservedOperations(IOInterposeObserver::OpNone) + , mIsEnabled(true) + { + MOZ_COUNT_CTOR(MasterList); + } + + ~MasterList() + { + MOZ_COUNT_DTOR(MasterList); + } + + inline void Disable() { mIsEnabled = false; } + inline void Enable() { mIsEnabled = true; } + + void Register(IOInterposeObserver::Operation aOp, + IOInterposeObserver* aObserver) + { + IOInterposer::AutoLock lock(mLock); + + ObserverLists* newLists = nullptr; + if (mObserverLists) { + newLists = new ObserverLists(*mObserverLists); + } else { + newLists = new ObserverLists(); + } + // You can register to observe multiple types of observations + // but you'll never be registered twice for the same observations. + if (aOp & IOInterposeObserver::OpCreateOrOpen && + !VectorContains(newLists->mCreateObservers, aObserver)) { + newLists->mCreateObservers.push_back(aObserver); + } + if (aOp & IOInterposeObserver::OpRead && + !VectorContains(newLists->mReadObservers, aObserver)) { + newLists->mReadObservers.push_back(aObserver); + } + if (aOp & IOInterposeObserver::OpWrite && + !VectorContains(newLists->mWriteObservers, aObserver)) { + newLists->mWriteObservers.push_back(aObserver); + } + if (aOp & IOInterposeObserver::OpFSync && + !VectorContains(newLists->mFSyncObservers, aObserver)) { + newLists->mFSyncObservers.push_back(aObserver); + } + if (aOp & IOInterposeObserver::OpStat && + !VectorContains(newLists->mStatObservers, aObserver)) { + newLists->mStatObservers.push_back(aObserver); + } + if (aOp & IOInterposeObserver::OpClose && + !VectorContains(newLists->mCloseObservers, aObserver)) { + newLists->mCloseObservers.push_back(aObserver); + } + if (aOp & IOInterposeObserver::OpNextStage && + !VectorContains(newLists->mStageObservers, aObserver)) { + newLists->mStageObservers.push_back(aObserver); + } + mObserverLists = newLists; + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations | aOp); + + mCurrentGeneration++; + } + + void Unregister(IOInterposeObserver::Operation aOp, + IOInterposeObserver* aObserver) + { + IOInterposer::AutoLock lock(mLock); + + ObserverLists* newLists = nullptr; + if (mObserverLists) { + newLists = new ObserverLists(*mObserverLists); + } else { + newLists = new ObserverLists(); + } + + if (aOp & IOInterposeObserver::OpCreateOrOpen) { + VectorRemove(newLists->mCreateObservers, aObserver); + if (newLists->mCreateObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~IOInterposeObserver::OpCreateOrOpen); + } + } + if (aOp & IOInterposeObserver::OpRead) { + VectorRemove(newLists->mReadObservers, aObserver); + if (newLists->mReadObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~IOInterposeObserver::OpRead); + } + } + if (aOp & IOInterposeObserver::OpWrite) { + VectorRemove(newLists->mWriteObservers, aObserver); + if (newLists->mWriteObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~IOInterposeObserver::OpWrite); + } + } + if (aOp & IOInterposeObserver::OpFSync) { + VectorRemove(newLists->mFSyncObservers, aObserver); + if (newLists->mFSyncObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~IOInterposeObserver::OpFSync); + } + } + if (aOp & IOInterposeObserver::OpStat) { + VectorRemove(newLists->mStatObservers, aObserver); + if (newLists->mStatObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~IOInterposeObserver::OpStat); + } + } + if (aOp & IOInterposeObserver::OpClose) { + VectorRemove(newLists->mCloseObservers, aObserver); + if (newLists->mCloseObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~IOInterposeObserver::OpClose); + } + } + if (aOp & IOInterposeObserver::OpNextStage) { + VectorRemove(newLists->mStageObservers, aObserver); + if (newLists->mStageObservers.empty()) { + mObservedOperations = + (IOInterposeObserver::Operation)(mObservedOperations & + ~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 master list. + IOInterposer::AutoLock lock(mLock); + aPtd.SetObserverLists(mCurrentGeneration, mObserverLists); + } + + inline bool IsObservedOperation(IOInterposeObserver::Operation aOp) + { + // The quick reader may observe that no locks are being employed here, + // hence the result of the operations is truly undefined. However, most + // computers will usually return either true or false, which is good enough. + // It is not a problem if we occasionally report more or less IO than is + // actually occurring. + return mIsEnabled && !!(mObservedOperations & aOp); + } + +private: + RefPtr<ObserverLists> mObserverLists; + // 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. + IOInterposer::Mutex mLock; + // Flags tracking which operations are being observed + IOInterposeObserver::Operation mObservedOperations; + // Used for quickly disabling everything by IOInterposer::Disable() + Atomic<bool> mIsEnabled; + // Used to inform threads that the master observer list has changed + Atomic<uint32_t> mCurrentGeneration; +}; + +// Special observation used by IOInterposer::EnteringNextStage() +class NextStageObservation : public IOInterposeObserver::Observation +{ +public: + NextStageObservation() + : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage, + "IOInterposer", false) + { + mStart = TimeStamp::Now(); + mEnd = mStart; + } +}; + +// List of observers registered +static StaticAutoPtr<MasterList> sMasterList; +static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData; +static bool sThreadLocalDataInitialized; +} // namespace + +IOInterposeObserver::Observation::Observation(Operation aOperation, + const char* aReference, + bool aShouldReport) + : mOperation(aOperation) + , mReference(aReference) + , mShouldReport(IOInterposer::IsObservedOperation(aOperation) && + aShouldReport) +{ + if (mShouldReport) { + mStart = TimeStamp::Now(); + } +} + +IOInterposeObserver::Observation::Observation(Operation aOperation, + const TimeStamp& aStart, + const TimeStamp& aEnd, + const char* aReference) + : mOperation(aOperation) + , mStart(aStart) + , mEnd(aEnd) + , mReference(aReference) + , mShouldReport(false) +{ +} + +const char* +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"; + } +} + +void +IOInterposeObserver::Observation::Report() +{ + if (mShouldReport) { + mEnd = TimeStamp::Now(); + IOInterposer::Report(*this); + } +} + +bool +IOInterposer::Init() +{ + // Don't initialize twice... + if (sMasterList) { + return true; + } + if (!sThreadLocalData.init()) { + return false; + } + sThreadLocalDataInitialized = true; + bool isMainThread = true; + RegisterCurrentThread(isMainThread); + sMasterList = new MasterList(); + + MainThreadIOLogger::Init(); + + // Now we initialize the various interposers depending on platform + InitPoisonIOInterposer(); + // We don't hook NSPR on Windows because PoisonIOInterposer captures a + // superset of the former's events. +#if !defined(XP_WIN) + InitNSPRIOInterposing(); +#endif + return true; +} + +bool +IOInterposeObserver::IsMainThread() +{ + if (!sThreadLocalDataInitialized) { + return false; + } + PerThreadData* ptd = sThreadLocalData.get(); + if (!ptd) { + return false; + } + 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(); + sMasterList = nullptr; +#endif +} + +void +IOInterposer::Disable() +{ + if (!sMasterList) { + return; + } + sMasterList->Disable(); +} + +void +IOInterposer::Enable() +{ + if (!sMasterList) { + return; + } + sMasterList->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 (!sMasterList) { + // If there is no longer a master list then we should clear the local one. + ptd->ClearObserverLists(); + return; + } + + sMasterList->Update(*ptd); + + // Don't try to report if there's nobody listening. + if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) { + return; + } + + ptd->CallObservers(aObservation); +} + +bool +IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp) +{ + return sMasterList && sMasterList->IsObservedOperation(aOp); +} + +void +IOInterposer::Register(IOInterposeObserver::Operation aOp, + IOInterposeObserver* aObserver) +{ + MOZ_ASSERT(aObserver); + if (!sMasterList || !aObserver) { + return; + } + + sMasterList->Register(aOp, aObserver); +} + +void +IOInterposer::Unregister(IOInterposeObserver::Operation aOp, + IOInterposeObserver* aObserver) +{ + if (!sMasterList) { + return; + } + + sMasterList->Unregister(aOp, aObserver); +} + +void +IOInterposer::RegisterCurrentThread(bool aIsMainThread) +{ + if (!sThreadLocalDataInitialized) { + return; + } + MOZ_ASSERT(!sThreadLocalData.get()); + PerThreadData* curThreadData = new PerThreadData(aIsMainThread); + sThreadLocalData.set(curThreadData); +} + +void +IOInterposer::UnregisterCurrentThread() +{ + if (!sThreadLocalDataInitialized) { + return; + } + PerThreadData* curThreadData = sThreadLocalData.get(); + MOZ_ASSERT(curThreadData); + sThreadLocalData.set(nullptr); + delete curThreadData; +} + +void +IOInterposer::EnteringNextStage() +{ + if (!sMasterList) { + return; + } + NextStageObservation observation; + Report(observation); +} + diff --git a/xpcom/build/IOInterposer.h b/xpcom/build/IOInterposer.h new file mode 100644 index 000000000..3634572a5 --- /dev/null +++ b/xpcom/build/IOInterposer.h @@ -0,0 +1,295 @@ +/* -*- 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/. */ + +#ifndef mozilla_IOInterposer_h +#define mozilla_IOInterposer_h + +#include "mozilla/Attributes.h" +#include "mozilla/GuardObjects.h" +#include "mozilla/TimeStamp.h" + +namespace mozilla { + +/** + * Interface for I/O interposer observers. This is separate from the + * IOInterposer because we have multiple uses for these observations. + */ +class IOInterposeObserver +{ +public: + enum Operation + { + OpNone = 0, + OpCreateOrOpen = (1 << 0), + OpRead = (1 << 1), + OpWrite = (1 << 2), + OpFSync = (1 << 3), + OpStat = (1 << 4), + OpClose = (1 << 5), + OpNextStage = (1 << 6), // Meta - used when leaving startup, entering shutdown + OpWriteFSync = (OpWrite | OpFSync), + OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose), + OpAllWithStaging = (OpAll | OpNextStage) + }; + + /** A representation of an I/O observation */ + class Observation + { + protected: + /** + * This constructor is for use by subclasses that are intended to take + * timing measurements via RAII. The |aShouldReport| parameter may be + * used to make the measurement and reporting conditional on the + * satisfaction of an arbitrary predicate that was evaluated + * in the subclass. Note that IOInterposer::IsObservedOperation() is + * always ANDed with aShouldReport, so the subclass does not need to + * include a call to that function explicitly. + */ + Observation(Operation aOperation, const char* aReference, + bool aShouldReport = true); + + public: + /** + * Since this constructor accepts start and end times, it does *not* take + * its own timings, nor does it report itself. + */ + Observation(Operation aOperation, const TimeStamp& aStart, + const TimeStamp& aEnd, const char* aReference); + + /** + * Operation observed, this is one of the individual Operation values. + * Combinations of these flags are only used when registering observers. + */ + Operation ObservedOperation() const { return mOperation; } + + /** + * Return the observed operation as a human-readable string. + */ + const char* ObservedOperationString() const; + + /** Time at which the I/O operation was started */ + TimeStamp Start() const { return mStart; } + + /** + * Time at which the I/O operation ended, for asynchronous methods this is + * the time at which the call initiating the asynchronous request returned. + */ + TimeStamp End() const { return mEnd; } + + /** + * Duration of the operation, for asynchronous I/O methods this is the + * duration of the call initiating the asynchronous request. + */ + TimeDuration Duration() const { return mEnd - mStart; } + + /** + * IO reference, function name or name of component (sqlite) that did IO + * this is in addition the generic operation. This attribute may be platform + * specific, but should only take a finite number of distinct values. + * E.g. sqlite-commit, CreateFile, NtReadFile, fread, fsync, mmap, etc. + * I.e. typically the platform specific function that did the IO. + */ + const char* Reference() const { return mReference; } + + /** Request filename associated with the I/O operation, null if unknown */ + virtual const char16_t* Filename() { return nullptr; } + + virtual ~Observation() {} + + protected: + void + Report(); + + Operation mOperation; + TimeStamp mStart; + TimeStamp mEnd; + const char* mReference; // Identifies the source of the Observation + bool mShouldReport; // Measure and report if true + }; + + /** + * Invoked whenever an implementation of the IOInterposeObserver should + * observe aObservation. Implement this and do your thing... + * But do consider if it is wise to use IO functions in this method, they are + * likely to cause recursion :) + * At least, see PoisonIOInterposer.h and register your handle as a debug file + * even, if you don't initialize the poison IO interposer, someone else might. + * + * Remark: Observations may occur on any thread. + */ + virtual void Observe(Observation& aObservation) = 0; + + virtual ~IOInterposeObserver() {} + +protected: + /** + * We don't use NS_IsMainThread() because we need to be able to determine the + * main thread outside of XPCOM Initialization. IOInterposer observers should + * call this function instead. + */ + static bool IsMainThread(); +}; + +/** + * These functions are responsible for ensuring that events are routed to the + * appropriate observers. + */ +namespace IOInterposer { + +/** + * This function must be called from the main-thread when no other threads are + * running before any of the other methods on this class may be used. + * + * IO reports can however, safely assume that IsObservedOperation() will + * return false until the IOInterposer is initialized. + * + * Remark, it's safe to call this method multiple times, so just call it when + * you to utilize IO interposing. + * + * Using the IOInterposerInit class is preferred to calling this directly. + */ +bool Init(); + +/** + * This function must be called from the main thread, and furthermore + * it must be called when no other threads are executing. Effectively + * restricting us to calling it only during shutdown. + * + * Callers should take care that no other consumers are subscribed to events, + * as these events will stop when this function is called. + * + * In practice, we don't use this method as the IOInterposer is used for + * late-write checks. + */ +void Clear(); + +/** + * This function immediately disables IOInterposer functionality in a fast, + * thread-safe manner. Primarily for use by the crash reporter. + */ +void Disable(); + +/** + * This function re-enables IOInterposer functionality in a fast, thread-safe + * manner. Primarily for use by the crash reporter. + */ +void Enable(); + +/** + * Report IO to registered observers. + * Notice that the reported operation must be either OpRead, OpWrite or + * OpFSync. You are not allowed to report an observation with OpWriteFSync or + * OpAll, these are just auxiliary values for use with Register(). + * + * If the IO call you're reporting does multiple things, write and fsync, you + * can choose to call Report() twice once with write and once with FSync. You + * may not call Report() with OpWriteFSync! The Observation::mOperation + * attribute is meant to be generic, not perfect. + * + * Notice that there is no reason to report an observation with an operation + * which is not being observed. Use IsObservedOperation() to check if the + * operation you are about to report is being observed. This is especially + * important if you are constructing expensive observations containing + * filename and full-path. + * + * Remark: Init() must be called before any IO is reported. But + * IsObservedOperation() will return false until Init() is called. + */ +void Report(IOInterposeObserver::Observation& aObservation); + +/** + * Return whether or not an operation is observed. Reporters should not + * report operations that are not being observed by anybody. This mechanism + * allows us to avoid reporting I/O when no observers are registered. + */ +bool IsObservedOperation(IOInterposeObserver::Operation aOp); + +/** + * Register IOInterposeObserver, the observer object will receive all + * observations for the given operation aOp. + * + * Remark: Init() must be called before observers are registered. + */ +void Register(IOInterposeObserver::Operation aOp, + IOInterposeObserver* aObserver); + +/** + * Unregister an IOInterposeObserver for a given operation + * Remark: It is always safe to unregister for all operations, even if yoú + * didn't register for them all. + * I.e. IOInterposer::Unregister(IOInterposeObserver::OpAll, aObserver) + * + * Remark: Init() must be called before observers are unregistered. + */ +void Unregister(IOInterposeObserver::Operation aOp, + IOInterposeObserver* aObserver); + +/** + * Registers the current thread with the IOInterposer. This must be done to + * ensure that per-thread data is created in an orderly fashion. + * We could have written this to initialize that data lazily, however this + * could have unintended consequences if a thread that is not aware of + * IOInterposer was implicitly registered: its per-thread data would never + * be deleted because it would not know to unregister itself. + * + * @param aIsMainThread true if IOInterposer should treat the current thread + * as the main thread. + */ +void RegisterCurrentThread(bool aIsMainThread = false); + +/** + * Unregisters the current thread with the IOInterposer. This is important + * to call when a thread is shutting down because it cleans up data that + * is stored in a TLS slot. + */ +void UnregisterCurrentThread(); + +/** + * Called to inform observers that the process has transitioned out of the + * startup stage or into the shutdown stage. Main thread only. + */ +void EnteringNextStage(); + +} // namespace IOInterposer + +class IOInterposerInit +{ +public: + IOInterposerInit() + { +#if !defined(RELEASE_OR_BETA) + IOInterposer::Init(); +#endif + } + + ~IOInterposerInit() + { +#if !defined(RELEASE_OR_BETA) + IOInterposer::Clear(); +#endif + } +}; + +class MOZ_RAII AutoIOInterposerDisable final +{ +public: + explicit AutoIOInterposerDisable(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + IOInterposer::Disable(); + } + ~AutoIOInterposerDisable() + { + IOInterposer::Enable(); + } + +private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} // namespace mozilla + +#endif // mozilla_IOInterposer_h diff --git a/xpcom/build/IOInterposerPrivate.h b/xpcom/build/IOInterposerPrivate.h new file mode 100644 index 000000000..549321062 --- /dev/null +++ b/xpcom/build/IOInterposerPrivate.h @@ -0,0 +1,167 @@ +/* -*- 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/. */ + +#ifndef xpcom_build_IOInterposerPrivate_h +#define xpcom_build_IOInterposerPrivate_h + +/* This header file contains declarations for helper classes that are + to be used exclusively by IOInterposer and its observers. This header + file is not to be used by anything else and MUST NOT be exported! */ + +#include <prcvar.h> +#include <prlock.h> + +namespace mozilla { +namespace IOInterposer { + +/** + * The following classes are simple wrappers for PRLock and PRCondVar. + * IOInterposer and friends use these instead of Mozilla::Mutex et al because + * of the fact that IOInterposer is permitted to run until the process + * terminates; we can't use anything that plugs into leak checkers or deadlock + * detectors because IOInterposer will outlive those and generate false + * positives. + */ + +class Monitor +{ +public: + Monitor() + : mLock(PR_NewLock()) + , mCondVar(PR_NewCondVar(mLock)) + { + } + + ~Monitor() + { + PR_DestroyCondVar(mCondVar); + mCondVar = nullptr; + PR_DestroyLock(mLock); + mLock = nullptr; + } + + void Lock() + { + PR_Lock(mLock); + } + + void Unlock() + { + PR_Unlock(mLock); + } + + bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT) + { + return PR_WaitCondVar(mCondVar, aTimeout) == PR_SUCCESS; + } + + bool Notify() + { + return PR_NotifyCondVar(mCondVar) == PR_SUCCESS; + } + +private: + PRLock* mLock; + PRCondVar* mCondVar; +}; + +class MonitorAutoLock +{ +public: + explicit MonitorAutoLock(Monitor& aMonitor) + : mMonitor(aMonitor) + { + mMonitor.Lock(); + } + + ~MonitorAutoLock() + { + mMonitor.Unlock(); + } + + bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT) + { + return mMonitor.Wait(aTimeout); + } + + bool Notify() + { + return mMonitor.Notify(); + } + +private: + Monitor& mMonitor; +}; + +class MonitorAutoUnlock +{ +public: + explicit MonitorAutoUnlock(Monitor& aMonitor) + : mMonitor(aMonitor) + { + mMonitor.Unlock(); + } + + ~MonitorAutoUnlock() + { + mMonitor.Lock(); + } + +private: + Monitor& mMonitor; +}; + +class Mutex +{ +public: + Mutex() + : mPRLock(PR_NewLock()) + { + } + + ~Mutex() + { + PR_DestroyLock(mPRLock); + mPRLock = nullptr; + } + + void Lock() + { + PR_Lock(mPRLock); + } + + void Unlock() + { + PR_Unlock(mPRLock); + } + +private: + PRLock* mPRLock; +}; + +class AutoLock +{ +public: + explicit AutoLock(Mutex& aLock) + : mLock(aLock) + { + mLock.Lock(); + } + + ~AutoLock() + { + mLock.Unlock(); + } + +private: + Mutex& mLock; +}; + +} // namespace IOInterposer +} // namespace mozilla + +#endif // xpcom_build_IOInterposerPrivate_h + diff --git a/xpcom/build/LateWriteChecks.cpp b/xpcom/build/LateWriteChecks.cpp new file mode 100644 index 000000000..69dbedc0f --- /dev/null +++ b/xpcom/build/LateWriteChecks.cpp @@ -0,0 +1,258 @@ +/* -*- 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 "mozilla/IOInterposer.h" +#include "mozilla/PoisonIOInterposer.h" +#include "mozilla/ProcessedStack.h" +#include "mozilla/SHA1.h" +#include "mozilla/Scoped.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" +#include "nsPrintfCString.h" +#include "mozilla/StackWalk.h" +#include "plstr.h" +#include "prio.h" + +#ifdef XP_WIN +#define NS_T(str) L ## str +#define NS_SLASH "\\" +#include <fcntl.h> +#include <io.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <windows.h> +#else +#define NS_SLASH "/" +#endif + +#include "LateWriteChecks.h" + +#define OBSERVE_LATE_WRITES + +using namespace mozilla; + +/*************************** Auxiliary Declarations ***************************/ + +// This a wrapper over a file descriptor that provides a Printf method and +// computes the sha1 of the data that passes through it. +class SHA1Stream +{ +public: + explicit SHA1Stream(FILE* aStream) + : mFile(aStream) + { + MozillaRegisterDebugFILE(mFile); + } + + void Printf(const char* aFormat, ...) + { + MOZ_ASSERT(mFile); + va_list list; + va_start(list, aFormat); + nsAutoCString str; + str.AppendPrintf(aFormat, list); + va_end(list); + mSHA1.update(str.get(), str.Length()); + fwrite(str.get(), 1, str.Length(), mFile); + } + void Finish(SHA1Sum::Hash& aHash) + { + int fd = fileno(mFile); + fflush(mFile); + MozillaUnRegisterDebugFD(fd); + fclose(mFile); + mSHA1.finish(aHash); + mFile = nullptr; + } +private: + FILE* mFile; + SHA1Sum mSHA1; +}; + +static void +RecordStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure) +{ + std::vector<uintptr_t>* stack = + static_cast<std::vector<uintptr_t>*>(aClosure); + stack->push_back(reinterpret_cast<uintptr_t>(aPC)); +} + +/**************************** Late-Write Observer ****************************/ + +/** + * An implementation of IOInterposeObserver to be registered with IOInterposer. + * This observer logs all writes as late writes. + */ +class LateWriteObserver final : public IOInterposeObserver +{ +public: + explicit LateWriteObserver(const char* aProfileDirectory) + : mProfileDirectory(PL_strdup(aProfileDirectory)) + { + } + ~LateWriteObserver() + { + PL_strfree(mProfileDirectory); + mProfileDirectory = nullptr; + } + + void Observe(IOInterposeObserver::Observation& aObservation); +private: + char* mProfileDirectory; +}; + +void +LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb) +{ +#ifdef OBSERVE_LATE_WRITES + // Crash if that is the shutdown check mode + if (gShutdownChecks == SCM_CRASH) { + MOZ_CRASH(); + } + + // If we have shutdown mode SCM_NOTHING or we can't record then abort + if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecordExtended()) { + return; + } + + // Write the stack and loaded libraries to a file. We can get here + // concurrently from many writes, so we use multiple temporary files. + std::vector<uintptr_t> rawStack; + + MozStackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0, + reinterpret_cast<void*>(&rawStack), 0, nullptr); + Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack); + + nsPrintfCString nameAux("%s%s%s", mProfileDirectory, + NS_SLASH, "Telemetry.LateWriteTmpXXXXXX"); + char* name; + nameAux.GetMutableData(&name); + + // We want the sha1 of the entire file, so please don't write to fd + // directly; use sha1Stream. + FILE* stream; +#ifdef XP_WIN + HANDLE hFile; + do { + // mkstemp isn't supported so keep trying until we get a file + int result = _mktemp_s(name, strlen(name) + 1); + hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, nullptr); + } while (GetLastError() == ERROR_FILE_EXISTS); + + if (hFile == INVALID_HANDLE_VALUE) { + NS_RUNTIMEABORT("Um, how did we get here?"); + } + + // http://support.microsoft.com/kb/139640 + int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND); + if (fd == -1) { + NS_RUNTIMEABORT("Um, how did we get here?"); + } + + stream = _fdopen(fd, "w"); +#else + int fd = mkstemp(name); + stream = fdopen(fd, "w"); +#endif + + SHA1Stream sha1Stream(stream); + + size_t numModules = stack.GetNumModules(); + sha1Stream.Printf("%u\n", (unsigned)numModules); + for (size_t i = 0; i < numModules; ++i) { + Telemetry::ProcessedStack::Module module = stack.GetModule(i); + sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(), + module.mName.c_str()); + } + + size_t numFrames = stack.GetStackSize(); + sha1Stream.Printf("%u\n", (unsigned)numFrames); + for (size_t i = 0; i < numFrames; ++i) { + const Telemetry::ProcessedStack::Frame& frame = stack.GetFrame(i); + // NOTE: We write the offsets, while the atos tool expects a value with + // the virtual address added. For example, running otool -l on the the firefox + // binary shows + // cmd LC_SEGMENT_64 + // cmdsize 632 + // segname __TEXT + // vmaddr 0x0000000100000000 + // so to print the line matching the offset 123 one has to run + // atos -o firefox 0x100000123. + sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset); + } + + SHA1Sum::Hash sha1; + sha1Stream.Finish(sha1); + + // Note: These files should be deleted by telemetry once it reads them. If + // there were no telemetry runs by the time we shut down, we just add files + // to the existing ones instead of replacing them. Given that each of these + // files is a bug to be fixed, that is probably the right thing to do. + + // We append the sha1 of the contents to the file name. This provides a simple + // client side deduplication. + nsPrintfCString finalName("%s%s", mProfileDirectory, + "/Telemetry.LateWriteFinal-"); + for (int i = 0; i < 20; ++i) { + finalName.AppendPrintf("%02x", sha1[i]); + } + PR_Delete(finalName.get()); + PR_Rename(name, finalName.get()); +#endif +} + +/******************************* Setup/Teardown *******************************/ + +static StaticAutoPtr<LateWriteObserver> sLateWriteObserver; + +namespace mozilla { + +void +InitLateWriteChecks() +{ + nsCOMPtr<nsIFile> mozFile; + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile)); + if (mozFile) { + nsAutoCString nativePath; + nsresult rv = mozFile->GetNativePath(nativePath); + if (NS_SUCCEEDED(rv) && nativePath.get()) { + sLateWriteObserver = new LateWriteObserver(nativePath.get()); + } + } +} + +void +BeginLateWriteChecks() +{ + if (sLateWriteObserver) { + IOInterposer::Register( + IOInterposeObserver::OpWriteFSync, + sLateWriteObserver + ); + } +} + +void +StopLateWriteChecks() +{ + if (sLateWriteObserver) { + IOInterposer::Unregister( + IOInterposeObserver::OpAll, + sLateWriteObserver + ); + // Deallocation would not be thread-safe, and StopLateWriteChecks() is + // called at shutdown and only in special cases. + // sLateWriteObserver = nullptr; + } +} + +} // namespace mozilla diff --git a/xpcom/build/LateWriteChecks.h b/xpcom/build/LateWriteChecks.h new file mode 100644 index 000000000..96d981cc1 --- /dev/null +++ b/xpcom/build/LateWriteChecks.h @@ -0,0 +1,60 @@ +/* -*- 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/. */ + +#ifndef mozilla_LateWriteChecks_h +#define mozilla_LateWriteChecks_h + +// This file, along with LateWriteChecks.cpp, serves to check for and report +// late writes. The idea is discover writes to the file system that happens +// during shutdown such that these maybe be moved forward and the process may be +// killed without waiting for static destructors. + +namespace mozilla { + +/** Different shutdown check modes */ +enum ShutdownChecksMode +{ + SCM_CRASH, /** Crash on shutdown check failure */ + SCM_RECORD, /** Record shutdown check violations */ + SCM_NOTHING /** Don't attempt any shutdown checks */ +}; + +/** + * Current shutdown check mode. + * This variable is defined and initialized in nsAppRunner.cpp + */ +extern ShutdownChecksMode gShutdownChecks; + +/** + * Allocate structures and acquire information from XPCOM necessary to do late + * write checks. This function must be invoked before BeginLateWriteChecks() + * and before XPCOM has stopped working. + */ +void InitLateWriteChecks(); + +/** + * Begin recording all writes as late-writes. This function should be called + * when all legitimate writes have occurred. This function does not rely on + * XPCOM as it is designed to be invoked during XPCOM shutdown. + * + * For late-write checks to work you must initialize one or more backends that + * reports IO through the IOInterposer API. PoisonIOInterposer would probably + * be the backend of choice in this case. + * + * Note: BeginLateWriteChecks() must have been invoked before this function. + */ +void BeginLateWriteChecks(); + +/** + * Stop recording all writes as late-writes, call this function when you want + * late-write checks to stop. I.e. exception handling, or the special case on + * Mac described in bug 826029. + */ +void StopLateWriteChecks(); + +} // namespace mozilla + +#endif // mozilla_LateWriteChecks_h diff --git a/xpcom/build/MainThreadIOLogger.cpp b/xpcom/build/MainThreadIOLogger.cpp new file mode 100644 index 000000000..0ca942cb9 --- /dev/null +++ b/xpcom/build/MainThreadIOLogger.cpp @@ -0,0 +1,225 @@ +/* -*- 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 "MainThreadIOLogger.h" + +#include "GeckoProfiler.h" +#include "IOInterposerPrivate.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/TimeStamp.h" +#include "nsAutoPtr.h" +#include "nsNativeCharsetUtils.h" + +/** + * This code uses NSPR stuff and STL containers because it must be detached + * from leak checking code; this observer runs until the process terminates. + */ + +#include <prenv.h> +#include <prprf.h> +#include <prthread.h> +#include <vector> + +namespace { + +struct ObservationWithStack +{ + ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs, + ProfilerBacktrace* aStack) + : mObservation(aObs) + , mStack(aStack) + { + const char16_t* filename = aObs.Filename(); + if (filename) { + mFilename = filename; + } + } + + mozilla::IOInterposeObserver::Observation mObservation; + ProfilerBacktrace* mStack; + nsString mFilename; +}; + +class MainThreadIOLoggerImpl final : public mozilla::IOInterposeObserver +{ +public: + MainThreadIOLoggerImpl(); + ~MainThreadIOLoggerImpl(); + + bool Init(); + + void Observe(Observation& aObservation); + +private: + static void sIOThreadFunc(void* aArg); + void IOThreadFunc(); + + TimeStamp mLogStartTime; + const char* mFileName; + PRThread* mIOThread; + IOInterposer::Monitor mMonitor; + bool mShutdownRequired; + std::vector<ObservationWithStack> mObservations; +}; + +static StaticAutoPtr<MainThreadIOLoggerImpl> sImpl; + +MainThreadIOLoggerImpl::MainThreadIOLoggerImpl() + : mFileName(nullptr) + , mIOThread(nullptr) + , mShutdownRequired(false) +{ +} + +MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl() +{ + if (!mIOThread) { + return; + } + { + // Scope for lock + IOInterposer::MonitorAutoLock lock(mMonitor); + mShutdownRequired = true; + lock.Notify(); + } + PR_JoinThread(mIOThread); + mIOThread = nullptr; +} + +bool +MainThreadIOLoggerImpl::Init() +{ + if (mFileName) { + // Already initialized + return true; + } + mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG"); + if (!mFileName) { + // Can't start + return false; + } + mIOThread = PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this, + PR_PRIORITY_LOW, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (!mIOThread) { + return false; + } + return true; +} + +/* static */ void +MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg) +{ + PR_SetCurrentThreadName("MainThreadIOLogger"); + MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg); + obj->IOThreadFunc(); +} + +void +MainThreadIOLoggerImpl::IOThreadFunc() +{ + PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, + PR_IRUSR | PR_IWUSR | PR_IRGRP); + if (!fd) { + IOInterposer::MonitorAutoLock lock(mMonitor); + mShutdownRequired = true; + std::vector<ObservationWithStack>().swap(mObservations); + return; + } + mLogStartTime = TimeStamp::Now(); + { + // Scope for lock + IOInterposer::MonitorAutoLock lock(mMonitor); + while (true) { + while (!mShutdownRequired && mObservations.empty()) { + lock.Wait(); + } + if (mShutdownRequired) { + break; + } + // Pull events off the shared array onto a local one + std::vector<ObservationWithStack> observationsToWrite; + observationsToWrite.swap(mObservations); + + // Release the lock so that we're not holding anybody up during I/O + IOInterposer::MonitorAutoUnlock unlock(mMonitor); + + // Now write the events. + for (auto i = observationsToWrite.begin(), e = observationsToWrite.end(); + i != e; ++i) { + if (i->mObservation.ObservedOperation() == OpNextStage) { + PR_fprintf(fd, "%f,NEXT-STAGE\n", + (TimeStamp::Now() - mLogStartTime).ToMilliseconds()); + continue; + } + double durationMs = i->mObservation.Duration().ToMilliseconds(); + nsAutoCString nativeFilename; + nativeFilename.AssignLiteral("(not available)"); + if (!i->mFilename.IsEmpty()) { + if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) { + nativeFilename.AssignLiteral("(conversion failed)"); + } + } + /** + * Format: + * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename + */ + if (PR_fprintf(fd, "%f,%s,%f,%s,%s\n", + (i->mObservation.Start() - mLogStartTime).ToMilliseconds(), + i->mObservation.ObservedOperationString(), durationMs, + i->mObservation.Reference(), nativeFilename.get()) > 0) { + ProfilerBacktrace* stack = i->mStack; + if (stack) { + // TODO: Write out the callstack + // (This will be added in a later bug) + profiler_free_backtrace(stack); + } + } + } + } + } + PR_Close(fd); +} + +void +MainThreadIOLoggerImpl::Observe(Observation& aObservation) +{ + if (!mFileName || !IsMainThread()) { + return; + } + IOInterposer::MonitorAutoLock lock(mMonitor); + if (mShutdownRequired) { + // The writer thread isn't running. Don't enqueue any more data. + return; + } + // Passing nullptr as aStack parameter for now + mObservations.push_back(ObservationWithStack(aObservation, nullptr)); + lock.Notify(); +} + +} // namespace + +namespace mozilla { + +namespace MainThreadIOLogger { + +bool +Init() +{ + nsAutoPtr<MainThreadIOLoggerImpl> impl(new MainThreadIOLoggerImpl()); + if (!impl->Init()) { + return false; + } + sImpl = impl.forget(); + IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl); + return true; +} + +} // namespace MainThreadIOLogger + +} // namespace mozilla + diff --git a/xpcom/build/MainThreadIOLogger.h b/xpcom/build/MainThreadIOLogger.h new file mode 100644 index 000000000..df9e30b53 --- /dev/null +++ b/xpcom/build/MainThreadIOLogger.h @@ -0,0 +1,19 @@ +/* -*- 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/. */ + +#ifndef mozilla_MainThreadIOLogger_h +#define mozilla_MainThreadIOLogger_h + +namespace mozilla { +namespace MainThreadIOLogger { + +bool Init(); + +} // namespace MainThreadIOLogger +} // namespace mozilla + +#endif // mozilla_MainThreadIOLogger_h + diff --git a/xpcom/build/NSPRInterposer.cpp b/xpcom/build/NSPRInterposer.cpp new file mode 100644 index 000000000..a7c53a97a --- /dev/null +++ b/xpcom/build/NSPRInterposer.cpp @@ -0,0 +1,185 @@ +/* -*- 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 "IOInterposer.h" +#include "NSPRInterposer.h" + +#include "prio.h" +#include "private/pprio.h" +#include "nsDebug.h" +#include "nscore.h" + +namespace { + +using namespace mozilla; + +/* Original IO methods */ +PRCloseFN sCloseFn = nullptr; +PRReadFN sReadFn = nullptr; +PRWriteFN sWriteFn = nullptr; +PRFsyncFN sFSyncFn = nullptr; +PRFileInfoFN sFileInfoFn = nullptr; +PRFileInfo64FN sFileInfo64Fn = nullptr; + +/** + * RAII class for timing the duration of an NSPR I/O call and reporting the + * result to the IOInterposeObserver API. + */ +class NSPRIOAutoObservation : public IOInterposeObserver::Observation +{ +public: + explicit NSPRIOAutoObservation(IOInterposeObserver::Operation aOp) + : IOInterposeObserver::Observation(aOp, "NSPRIOInterposer") + { + } + + ~NSPRIOAutoObservation() + { + Report(); + } +}; + +PRStatus PR_CALLBACK +interposedClose(PRFileDesc* aFd) +{ + // If we don't have a valid original function pointer something is very wrong. + NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL"); + + NSPRIOAutoObservation timer(IOInterposeObserver::OpClose); + return sCloseFn(aFd); +} + +int32_t PR_CALLBACK +interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt) +{ + // If we don't have a valid original function pointer something is very wrong. + NS_ASSERTION(sReadFn, "NSPR IO Interposing: sReadFn is NULL"); + + NSPRIOAutoObservation timer(IOInterposeObserver::OpRead); + return sReadFn(aFd, aBuf, aAmt); +} + +int32_t PR_CALLBACK +interposedWrite(PRFileDesc* aFd, const void* aBuf, int32_t aAmt) +{ + // If we don't have a valid original function pointer something is very wrong. + NS_ASSERTION(sWriteFn, "NSPR IO Interposing: sWriteFn is NULL"); + + NSPRIOAutoObservation timer(IOInterposeObserver::OpWrite); + return sWriteFn(aFd, aBuf, aAmt); +} + +PRStatus PR_CALLBACK +interposedFSync(PRFileDesc* aFd) +{ + // If we don't have a valid original function pointer something is very wrong. + NS_ASSERTION(sFSyncFn, "NSPR IO Interposing: sFSyncFn is NULL"); + + NSPRIOAutoObservation timer(IOInterposeObserver::OpFSync); + return sFSyncFn(aFd); +} + +PRStatus PR_CALLBACK +interposedFileInfo(PRFileDesc* aFd, PRFileInfo* aInfo) +{ + // If we don't have a valid original function pointer something is very wrong. + NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL"); + + NSPRIOAutoObservation timer(IOInterposeObserver::OpStat); + return sFileInfoFn(aFd, aInfo); +} + +PRStatus PR_CALLBACK +interposedFileInfo64(PRFileDesc* aFd, PRFileInfo64* aInfo) +{ + // If we don't have a valid original function pointer something is very wrong. + NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL"); + + NSPRIOAutoObservation timer(IOInterposeObserver::OpStat); + return sFileInfo64Fn(aFd, aInfo); +} + +} // namespace + +namespace mozilla { + +void +InitNSPRIOInterposing() +{ + // Check that we have not interposed any of the IO methods before + MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn && + !sFileInfo64Fn); + + // We can't actually use this assertion because we initialize this code + // before XPCOM is initialized, so NS_IsMainThread() always returns false. + // MOZ_ASSERT(NS_IsMainThread()); + + // Get IO methods from NSPR and const cast the structure so we can modify it. + PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods()); + + // Something is badly wrong if we don't get IO methods... However, we don't + // want to crash over that in non-debug builds. This is unlikely to happen + // so an assert is enough, no need to report it to the caller. + MOZ_ASSERT(methods); + if (!methods) { + return; + } + + // Store original functions + sCloseFn = methods->close; + sReadFn = methods->read; + sWriteFn = methods->write; + sFSyncFn = methods->fsync; + sFileInfoFn = methods->fileInfo; + sFileInfo64Fn = methods->fileInfo64; + + // Overwrite with our interposed functions + methods->close = &interposedClose; + methods->read = &interposedRead; + methods->write = &interposedWrite; + methods->fsync = &interposedFSync; + methods->fileInfo = &interposedFileInfo; + methods->fileInfo64 = &interposedFileInfo64; +} + +void +ClearNSPRIOInterposing() +{ + // If we have already cleared IO interposing, or not initialized it this is + // actually bad. + MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn && + sFileInfo64Fn); + + // Get IO methods from NSPR and const cast the structure so we can modify it. + PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods()); + + // Something is badly wrong if we don't get IO methods... However, we don't + // want to crash over that in non-debug builds. This is unlikely to happen + // so an assert is enough, no need to report it to the caller. + MOZ_ASSERT(methods); + if (!methods) { + return; + } + + // Restore original functions + methods->close = sCloseFn; + methods->read = sReadFn; + methods->write = sWriteFn; + methods->fsync = sFSyncFn; + methods->fileInfo = sFileInfoFn; + methods->fileInfo64 = sFileInfo64Fn; + + // Forget about original functions + sCloseFn = nullptr; + sReadFn = nullptr; + sWriteFn = nullptr; + sFSyncFn = nullptr; + sFileInfoFn = nullptr; + sFileInfo64Fn = nullptr; +} + +} // namespace mozilla + diff --git a/xpcom/build/NSPRInterposer.h b/xpcom/build/NSPRInterposer.h new file mode 100644 index 000000000..2b8715077 --- /dev/null +++ b/xpcom/build/NSPRInterposer.h @@ -0,0 +1,28 @@ +/* -*- 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/. */ + +#ifndef NSPRINTERPOSER_H_ +#define NSPRINTERPOSER_H_ + +namespace mozilla { + +/** + * Initialize IO interposing for NSPR. This will report NSPR read, writes and + * fsyncs to the IOInterposerObserver. It is only safe to call this from the + * main-thread when no other threads are running. + */ +void InitNSPRIOInterposing(); + +/** + * Removes interception of NSPR IO methods as setup by InitNSPRIOInterposing. + * Note, that it is only safe to call this on the main-thread when all other + * threads have stopped. Which is typically the case at shutdown. + */ +void ClearNSPRIOInterposing(); + +} // namespace mozilla + +#endif // NSPRINTERPOSER_H_ diff --git a/xpcom/build/Omnijar.cpp b/xpcom/build/Omnijar.cpp new file mode 100644 index 000000000..e1a8a1e5b --- /dev/null +++ b/xpcom/build/Omnijar.cpp @@ -0,0 +1,188 @@ +/* -*- 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 "Omnijar.h" + +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIFile.h" +#include "nsZipArchive.h" +#include "nsNetUtil.h" + +namespace mozilla { + +StaticRefPtr<nsIFile> Omnijar::sPath[2]; +StaticRefPtr<nsZipArchive> Omnijar::sReader[2]; +StaticRefPtr<nsZipArchive> Omnijar::sOuterReader[2]; +bool Omnijar::sInitialized = false; +bool Omnijar::sIsUnified = false; + +static const char* sProp[2] = { + NS_GRE_DIR, NS_XPCOM_CURRENT_PROCESS_DIR +}; + +#define SPROP(Type) ((Type == mozilla::Omnijar::GRE) ? sProp[GRE] : sProp[APP]) + +void +Omnijar::CleanUpOne(Type aType) +{ + if (sReader[aType]) { + sReader[aType]->CloseArchive(); + sReader[aType] = nullptr; + } + if (sOuterReader[aType]) { + sOuterReader[aType]->CloseArchive(); + sOuterReader[aType] = nullptr; + } + sPath[aType] = nullptr; +} + +void +Omnijar::InitOne(nsIFile* aPath, Type aType) +{ + nsCOMPtr<nsIFile> file; + if (aPath) { + file = aPath; + } else { + nsCOMPtr<nsIFile> dir; + nsDirectoryService::gService->Get(SPROP(aType), NS_GET_IID(nsIFile), + getter_AddRefs(dir)); + NS_NAMED_LITERAL_CSTRING(kOmnijarName, NS_STRINGIFY(OMNIJAR_NAME)); + if (NS_FAILED(dir->Clone(getter_AddRefs(file))) || + NS_FAILED(file->AppendNative(kOmnijarName))) { + return; + } + } + bool isFile; + if (NS_FAILED(file->IsFile(&isFile)) || !isFile) { + // If we're not using an omni.jar for GRE, and we don't have an + // omni.jar for APP, check if both directories are the same. + if ((aType == APP) && (!sPath[GRE])) { + nsCOMPtr<nsIFile> greDir, appDir; + bool equals; + nsDirectoryService::gService->Get(sProp[GRE], NS_GET_IID(nsIFile), + getter_AddRefs(greDir)); + nsDirectoryService::gService->Get(sProp[APP], NS_GET_IID(nsIFile), + getter_AddRefs(appDir)); + if (NS_SUCCEEDED(greDir->Equals(appDir, &equals)) && equals) { + sIsUnified = true; + } + } + return; + } + + bool equals; + if ((aType == APP) && (sPath[GRE]) && + NS_SUCCEEDED(sPath[GRE]->Equals(file, &equals)) && equals) { + // If we're using omni.jar on both GRE and APP and their path + // is the same, we're in the unified case. + sIsUnified = true; + return; + } + + RefPtr<nsZipArchive> zipReader = new nsZipArchive(); + if (NS_FAILED(zipReader->OpenArchive(file))) { + return; + } + + RefPtr<nsZipArchive> outerReader; + RefPtr<nsZipHandle> handle; + if (NS_SUCCEEDED(nsZipHandle::Init(zipReader, NS_STRINGIFY(OMNIJAR_NAME), + getter_AddRefs(handle)))) { + outerReader = zipReader; + zipReader = new nsZipArchive(); + if (NS_FAILED(zipReader->OpenArchive(handle))) { + return; + } + } + + CleanUpOne(aType); + sReader[aType] = zipReader; + sOuterReader[aType] = outerReader; + sPath[aType] = file; +} + +void +Omnijar::Init(nsIFile* aGrePath, nsIFile* aAppPath) +{ + InitOne(aGrePath, GRE); + InitOne(aAppPath, APP); + sInitialized = true; +} + +void +Omnijar::CleanUp() +{ + CleanUpOne(GRE); + CleanUpOne(APP); + sInitialized = false; +} + +already_AddRefed<nsZipArchive> +Omnijar::GetReader(nsIFile* aPath) +{ + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + + bool equals; + nsresult rv; + + if (sPath[GRE]) { + rv = sPath[GRE]->Equals(aPath, &equals); + if (NS_SUCCEEDED(rv) && equals) { + return IsNested(GRE) ? GetOuterReader(GRE) : GetReader(GRE); + } + } + if (sPath[APP]) { + rv = sPath[APP]->Equals(aPath, &equals); + if (NS_SUCCEEDED(rv) && equals) { + return IsNested(APP) ? GetOuterReader(APP) : GetReader(APP); + } + } + return nullptr; +} + +nsresult +Omnijar::GetURIString(Type aType, nsACString& aResult) +{ + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + + aResult.Truncate(); + + // Return an empty string for APP in the unified case. + if ((aType == APP) && sIsUnified) { + return NS_OK; + } + + nsAutoCString omniJarSpec; + if (sPath[aType]) { + nsresult rv = NS_GetURLSpecFromActualFile(sPath[aType], omniJarSpec); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + aResult = "jar:"; + if (IsNested(aType)) { + aResult += "jar:"; + } + aResult += omniJarSpec; + aResult += "!"; + if (IsNested(aType)) { + aResult += "/" NS_STRINGIFY(OMNIJAR_NAME) "!"; + } + } else { + nsCOMPtr<nsIFile> dir; + nsDirectoryService::gService->Get(SPROP(aType), NS_GET_IID(nsIFile), + getter_AddRefs(dir)); + nsresult rv = NS_GetURLSpecFromActualFile(dir, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + aResult += "/"; + return NS_OK; +} + +} /* namespace mozilla */ diff --git a/xpcom/build/Omnijar.h b/xpcom/build/Omnijar.h new file mode 100644 index 000000000..35a208bae --- /dev/null +++ b/xpcom/build/Omnijar.h @@ -0,0 +1,160 @@ +/* -*- 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/. */ + +#ifndef mozilla_Omnijar_h +#define mozilla_Omnijar_h + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIFile.h" +#include "nsZipArchive.h" + +#include "mozilla/StaticPtr.h" + +namespace mozilla { + +class Omnijar +{ +private: + /** + * Store an nsIFile for an omni.jar. We can store two paths here, one + * for GRE (corresponding to resource://gre/) and one for APP + * (corresponding to resource:/// and resource://app/), but only + * store one when both point to the same location (unified). + */ + static StaticRefPtr<nsIFile> sPath[2]; + + /** + * Cached nsZipArchives for the corresponding sPath + */ + static StaticRefPtr<nsZipArchive> sReader[2]; + + /** + * Cached nsZipArchives for the outer jar, when using nested jars. + * Otherwise nullptr. + */ + static StaticRefPtr<nsZipArchive> sOuterReader[2]; + + /** + * Has Omnijar::Init() been called? + */ + static bool sInitialized; + + /** + * Is using unified GRE/APP jar? + */ + static bool sIsUnified; + +public: + enum Type + { + GRE = 0, + APP = 1 + }; + +private: + /** + * Returns whether we are using nested jars. + */ + static inline bool IsNested(Type aType) + { + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + return !!sOuterReader[aType]; + } + + /** + * Returns a nsZipArchive pointer for the outer jar file when using nested + * jars. Returns nullptr in the same cases GetPath() would, or if not using + * nested jars. + */ + static inline already_AddRefed<nsZipArchive> GetOuterReader(Type aType) + { + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + RefPtr<nsZipArchive> reader = sOuterReader[aType].get(); + return reader.forget(); + } + +public: + /** + * Returns whether SetBase has been called at least once with + * a valid nsIFile + */ + static inline bool IsInitialized() { return sInitialized; } + + /** + * Initializes the Omnijar API with the given directory or file for GRE and + * APP. Each of the paths given can be: + * - a file path, pointing to the omnijar file, + * - a directory path, pointing to a directory containing an "omni.jar" file, + * - nullptr for autodetection of an "omni.jar" file. + */ + static void Init(nsIFile* aGrePath = nullptr, nsIFile* aAppPath = nullptr); + + /** + * Cleans up the Omnijar API + */ + static void CleanUp(); + + /** + * Returns an nsIFile pointing to the omni.jar file for GRE or APP. + * Returns nullptr when there is no corresponding omni.jar. + * Also returns nullptr for APP in the unified case. + */ + static inline already_AddRefed<nsIFile> GetPath(Type aType) + { + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + nsCOMPtr<nsIFile> path = sPath[aType].get(); + return path.forget(); + } + + /** + * Returns whether GRE or APP use an omni.jar. Returns PR_False for + * APP when using an omni.jar in the unified case. + */ + static inline bool HasOmnijar(Type aType) + { + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + return !!sPath[aType]; + } + + /** + * Returns a nsZipArchive pointer for the omni.jar file for GRE or + * APP. Returns nullptr in the same cases GetPath() would. + */ + static inline already_AddRefed<nsZipArchive> GetReader(Type aType) + { + MOZ_ASSERT(IsInitialized(), "Omnijar not initialized"); + RefPtr<nsZipArchive> reader = sReader[aType].get(); + return reader.forget(); + } + + /** + * Returns a nsZipArchive pointer for the given path IAOI the given + * path is the omni.jar for either GRE or APP. + */ + static already_AddRefed<nsZipArchive> GetReader(nsIFile* aPath); + + /** + * Returns the URI string corresponding to the omni.jar or directory + * for GRE or APP. i.e. jar:/path/to/omni.jar!/ for omni.jar and + * /path/to/base/dir/ otherwise. Returns an empty string for APP in + * the unified case. + * The returned URI is guaranteed to end with a slash. + */ + static nsresult GetURIString(Type aType, nsACString& aResult); + +private: + /** + * Used internally, respectively by Init() and CleanUp() + */ + static void InitOne(nsIFile* aPath, Type aType); + static void CleanUpOne(Type aType); +}; /* class Omnijar */ + +} /* namespace mozilla */ + +#endif /* mozilla_Omnijar_h */ diff --git a/xpcom/build/PoisonIOInterposer.h b/xpcom/build/PoisonIOInterposer.h new file mode 100644 index 000000000..4a4d1426d --- /dev/null +++ b/xpcom/build/PoisonIOInterposer.h @@ -0,0 +1,91 @@ +/* -*- 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/. */ + +#ifndef mozilla_PoisonIOInterposer_h +#define mozilla_PoisonIOInterposer_h + +#include "mozilla/Types.h" +#include <stdio.h> + +MOZ_BEGIN_EXTERN_C + +/** Register file handle to be ignored by poisoning IO interposer. This function + * and the corresponding UnRegister function are necessary for exchange of handles + * between binaries not using the same CRT on Windows (which happens when one of + * them links the static CRT). In such cases, giving file descriptors or FILEs + * doesn't work because _get_osfhandle fails with "invalid parameter". */ +void MozillaRegisterDebugHandle(intptr_t aHandle); + +/** Register file descriptor to be ignored by poisoning IO interposer */ +void MozillaRegisterDebugFD(int aFd); + +/** Register file to be ignored by poisoning IO interposer */ +void MozillaRegisterDebugFILE(FILE* aFile); + +/** Unregister file handle from being ignored by poisoning IO interposer */ +void MozillaUnRegisterDebugHandle(intptr_t aHandle); + +/** Unregister file descriptor from being ignored by poisoning IO interposer */ +void MozillaUnRegisterDebugFD(int aFd); + +/** Unregister file from being ignored by poisoning IO interposer */ +void MozillaUnRegisterDebugFILE(FILE* aFile); + +MOZ_END_EXTERN_C + +#if defined(XP_WIN) || defined(XP_MACOSX) + +#ifdef __cplusplus +namespace mozilla { + +/** + * Check if a file is registered as a debug file. + */ +bool IsDebugFile(intptr_t aFileID); + +/** + * Initialize IO poisoning, this is only safe to do on the main-thread when no + * other threads are running. + * + * Please, note that this probably has performance implications as all + */ +void InitPoisonIOInterposer(); + +#ifdef XP_MACOSX +/** + * Check that writes are dirty before reporting I/O (Mac OS X only) + * This is necessary for late-write checks on Mac OS X, but reading the buffer + * from file to see if we're writing dirty bits is expensive, so we don't want + * to do this for everything else that uses + */ +void OnlyReportDirtyWrites(); +#endif /* XP_MACOSX */ + +/** + * Clear IO poisoning, this is only safe to do on the main-thread when no other + * threads are running. + */ +void ClearPoisonIOInterposer(); + +} // namespace mozilla +#endif /* __cplusplus */ + +#else /* XP_WIN || XP_MACOSX */ + +#ifdef __cplusplus +namespace mozilla { +inline bool IsDebugFile(intptr_t aFileID) { return true; } +inline void InitPoisonIOInterposer() {} +inline void ClearPoisonIOInterposer() {} +#ifdef XP_MACOSX +inline void OnlyReportDirtyWrites() {} +#endif /* XP_MACOSX */ +} // namespace mozilla +#endif /* __cplusplus */ + +#endif /* XP_WIN || XP_MACOSX */ + +#endif // mozilla_PoisonIOInterposer_h diff --git a/xpcom/build/PoisonIOInterposerBase.cpp b/xpcom/build/PoisonIOInterposerBase.cpp new file mode 100644 index 000000000..0e5754392 --- /dev/null +++ b/xpcom/build/PoisonIOInterposerBase.cpp @@ -0,0 +1,291 @@ +/* -*- 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 "mozilla/Mutex.h" +#include "mozilla/Scoped.h" +#include "mozilla/UniquePtr.h" + +#include <algorithm> + +#include "PoisonIOInterposer.h" + +#ifdef MOZ_REPLACE_MALLOC +#include "replace_malloc_bridge.h" +#endif + +// Auxiliary method to convert file descriptors to ids +#if defined(XP_WIN32) +#include <io.h> +inline intptr_t +FileDescriptorToHandle(int aFd) +{ + return _get_osfhandle(aFd); +} +#else +inline intptr_t +FileDescriptorToHandle(int aFd) +{ + return aFd; +} +#endif /* if not XP_WIN32 */ + +using namespace mozilla; + +namespace { +struct DebugFilesAutoLockTraits +{ + typedef PRLock* type; + typedef const PRLock* const_type; + static const_type empty() { return nullptr; } + static void release(type aL) { PR_Unlock(aL); } +}; + +class DebugFilesAutoLock : public Scoped<DebugFilesAutoLockTraits> +{ + static PRLock* Lock; +public: + static void Clear(); + static PRLock* getDebugFileIDsLock() + { + // On windows this static is not thread safe, but we know that the first + // call is from + // * An early registration of a debug FD or + // * The call to InitWritePoisoning. + // Since the early debug FDs are logs created early in the main thread + // and no writes are trapped before InitWritePoisoning, we are safe. + if (!Lock) { + Lock = PR_NewLock(); + } + + // We have to use something lower level than a mutex. If we don't, we + // can get recursive in here when called from logging a call to free. + return Lock; + } + + DebugFilesAutoLock() + : Scoped<DebugFilesAutoLockTraits>(getDebugFileIDsLock()) + { + PR_Lock(get()); + } +}; + +PRLock* DebugFilesAutoLock::Lock; +void +DebugFilesAutoLock::Clear() +{ + MOZ_ASSERT(Lock != nullptr); + Lock = nullptr; +} + +// The ChunkedList<T> class implements, at the high level, a non-iterable +// list of instances of T. Its goal is to be somehow minimalist for the +// use case of storing the debug files handles here, with the property of +// not requiring a lock to look up whether it contains a specific value. +// It is also chunked in blocks of chunk_size bytes so that its +// initialization doesn't require a memory allocation, while keeping the +// possibility to increase its size as necessary. Note that chunks are +// never deallocated (except in the destructor). +// All operations are essentially O(N) but N is not expected to be large +// enough to matter. +template <typename T, size_t chunk_size=64> +class ChunkedList { + struct ListChunk { + static const size_t kLength = \ + (chunk_size - sizeof(ListChunk*)) / sizeof(mozilla::Atomic<T>); + + mozilla::Atomic<T> mElements[kLength]; + mozilla::UniquePtr<ListChunk> mNext; + + ListChunk() : mNext(nullptr) {} + }; + + ListChunk mList; + mozilla::Atomic<size_t> mLength; + +public: + ChunkedList() : mLength(0) {} + + ~ChunkedList() { + // There can be writes happening after this destructor runs, so keep + // the list contents and don't reset mLength. But if there are more + // elements left than the first chunk can hold, then all hell breaks + // loose for any write that would happen after that because any extra + // chunk would be deallocated, so just crash in that case. + MOZ_RELEASE_ASSERT(mLength <= ListChunk::kLength); + } + + // Add an element at the end of the last chunk of the list. Create a new + // chunk if there is not enough room. + // This is not thread-safe with another thread calling Add or Remove. + void Add(T aValue) + { + ListChunk *list = &mList; + size_t position = mLength; + for (; position >= ListChunk::kLength; position -= ListChunk::kLength) { + if (!list->mNext) { + list->mNext.reset(new ListChunk()); + } + list = list->mNext.get(); + } + // Use an order of operations that ensures any racing Contains call + // can't be hurt. + list->mElements[position] = aValue; + mLength++; + } + + // Remove an element from the list by replacing it with the last element + // of the list, and then shrinking the list. + // This is not thread-safe with another thread calling Add or Remove. + void Remove(T aValue) + { + if (!mLength) { + return; + } + ListChunk *list = &mList; + size_t last = mLength - 1; + do { + size_t position = 0; + // Look for an element matching the given value. + for (; position < ListChunk::kLength; position++) { + if (aValue == list->mElements[position]) { + ListChunk *last_list = list; + // Look for the last element in the list, starting from where we are + // instead of starting over. + for (; last >= ListChunk::kLength; last -= ListChunk::kLength) { + last_list = last_list->mNext.get(); + } + // Use an order of operations that ensures any racing Contains call + // can't be hurt. + T value = last_list->mElements[last]; + list->mElements[position] = value; + mLength--; + return; + } + } + last -= ListChunk::kLength; + list = list->mNext.get(); + } while (list); + } + + // Returns whether the list contains the given value. It is meant to be safe + // to use without locking, with the tradeoff of being not entirely accurate + // if another thread adds or removes an element while this function runs. + bool Contains(T aValue) + { + ListChunk *list = &mList; + // Fix the range of the lookup to whatever the list length is when the + // function is called. + size_t length = mLength; + do { + size_t list_length = ListChunk::kLength; + list_length = std::min(list_length, length); + for (size_t position = 0; position < list_length; position++) { + if (aValue == list->mElements[position]) { + return true; + } + } + length -= ListChunk::kLength; + list = list->mNext.get(); + } while (list); + + return false; + } +}; + +typedef ChunkedList<intptr_t> FdList; + +// Return a list used to hold the IDs of the current debug files. On unix +// an ID is a file descriptor. On Windows it is a file HANDLE. +FdList& +getDebugFileIDs() +{ + static FdList DebugFileIDs; + return DebugFileIDs; +} + + +} // namespace + +namespace mozilla { + +// Auxiliary Method to test if a file descriptor is registered to be ignored +// by the poisoning IO interposer +bool +IsDebugFile(intptr_t aFileID) +{ + return getDebugFileIDs().Contains(aFileID); +} + +} // namespace mozilla + +extern "C" { + +void +MozillaRegisterDebugHandle(intptr_t aHandle) +{ + DebugFilesAutoLock lockedScope; + FdList& DebugFileIDs = getDebugFileIDs(); + MOZ_ASSERT(!DebugFileIDs.Contains(aHandle)); + DebugFileIDs.Add(aHandle); +} + +void +MozillaRegisterDebugFD(int aFd) +{ + MozillaRegisterDebugHandle(FileDescriptorToHandle(aFd)); +} + +void +MozillaRegisterDebugFILE(FILE* aFile) +{ + int fd = fileno(aFile); + if (fd == 1 || fd == 2) { + return; + } + MozillaRegisterDebugFD(fd); +} + +void +MozillaUnRegisterDebugHandle(intptr_t aHandle) +{ + DebugFilesAutoLock lockedScope; + FdList& DebugFileIDs = getDebugFileIDs(); + MOZ_ASSERT(DebugFileIDs.Contains(aHandle)); + DebugFileIDs.Remove(aHandle); +} + +void +MozillaUnRegisterDebugFD(int aFd) +{ + MozillaUnRegisterDebugHandle(FileDescriptorToHandle(aFd)); +} + +void +MozillaUnRegisterDebugFILE(FILE* aFile) +{ + int fd = fileno(aFile); + if (fd == 1 || fd == 2) { + return; + } + fflush(aFile); + MozillaUnRegisterDebugFD(fd); +} + +} // extern "C" + +#ifdef MOZ_REPLACE_MALLOC +void +DebugFdRegistry::RegisterHandle(intptr_t aHandle) +{ + MozillaRegisterDebugHandle(aHandle); +} + +void +DebugFdRegistry::UnRegisterHandle(intptr_t aHandle) +{ + MozillaUnRegisterDebugHandle(aHandle); +} +#endif diff --git a/xpcom/build/PoisonIOInterposerMac.cpp b/xpcom/build/PoisonIOInterposerMac.cpp new file mode 100644 index 000000000..966269495 --- /dev/null +++ b/xpcom/build/PoisonIOInterposerMac.cpp @@ -0,0 +1,386 @@ +/* -*- 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 "PoisonIOInterposer.h" +#include "mach_override.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/Mutex.h" +#include "mozilla/ProcessedStack.h" +#include "mozilla/Telemetry.h" +#include "mozilla/UniquePtrExtensions.h" +#include "nsPrintfCString.h" +#include "mozilla/StackWalk.h" +#include "nsTraceRefcnt.h" +#include "plstr.h" +#include "prio.h" + +#include <algorithm> +#include <vector> + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <aio.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> + +#ifdef MOZ_REPLACE_MALLOC +#include "replace_malloc_bridge.h" +#endif + +namespace { + +using namespace mozilla; + +// Bit tracking if poisoned writes are enabled +static bool sIsEnabled = false; + +// Check if writes are dirty before reporting IO +static bool sOnlyReportDirtyWrites = false; + +// Routines for write validation +bool IsValidWrite(int aFd, const void* aWbuf, size_t aCount); +bool IsIPCWrite(int aFd, const struct stat& aBuf); + +/******************************** IO AutoTimer ********************************/ + +/** + * RAII class for timing the duration of an I/O call and reporting the result + * to the IOInterposeObserver API. + */ +class MacIOAutoObservation : public IOInterposeObserver::Observation +{ +public: + MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd) + : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled && + !IsDebugFile(aFd)) + , mFd(aFd) + , mHasQueriedFilename(false) + , mFilename(nullptr) + { + } + + MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd, + const void* aBuf, size_t aCount) + : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled && + !IsDebugFile(aFd) && + IsValidWrite(aFd, aBuf, aCount)) + , mFd(aFd) + , mHasQueriedFilename(false) + , mFilename(nullptr) + { + } + + // Custom implementation of IOInterposeObserver::Observation::Filename + const char16_t* Filename() override; + + ~MacIOAutoObservation() + { + Report(); + if (mFilename) { + free(mFilename); + mFilename = nullptr; + } + } + +private: + int mFd; + bool mHasQueriedFilename; + char16_t* mFilename; + static const char* sReference; +}; + +const char* MacIOAutoObservation::sReference = "PoisonIOInterposer"; + +// Get filename for this observation +const char16_t* +MacIOAutoObservation::Filename() +{ + // If mHasQueriedFilename is true, then we already have it + if (mHasQueriedFilename) { + return mFilename; + } + char filename[MAXPATHLEN]; + if (fcntl(mFd, F_GETPATH, filename) != -1) { + mFilename = UTF8ToNewUnicode(nsDependentCString(filename)); + } else { + mFilename = nullptr; + } + mHasQueriedFilename = true; + + // Return filename + return mFilename; +} + +/****************************** Write Validation ******************************/ + +// We want to detect "actual" writes, not IPC. Some IPC mechanisms are +// implemented with file descriptors, so filter them out. +bool +IsIPCWrite(int aFd, const struct stat& aBuf) +{ + if ((aBuf.st_mode & S_IFMT) == S_IFIFO) { + return true; + } + + if ((aBuf.st_mode & S_IFMT) != S_IFSOCK) { + return false; + } + + sockaddr_storage address; + socklen_t len = sizeof(address); + if (getsockname(aFd, (sockaddr*)&address, &len) != 0) { + return true; // Ignore the aFd if we can't find what it is. + } + + return address.ss_family == AF_UNIX; +} + +// We want to report actual disk IO not things that don't move bits on the disk +bool +IsValidWrite(int aFd, const void* aWbuf, size_t aCount) +{ + // Ignore writes of zero bytes, Firefox does some during shutdown. + if (aCount == 0) { + return false; + } + + { + struct stat buf; + int rv = fstat(aFd, &buf); + if (rv != 0) { + return true; + } + + if (IsIPCWrite(aFd, buf)) { + return false; + } + } + + // For writev we pass a nullptr aWbuf. We should only get here from + // dbm, and it uses write, so assert that we have aWbuf. + if (!aWbuf) { + return true; + } + + // Break, here if we're allowed to report non-dirty writes + if (!sOnlyReportDirtyWrites) { + return true; + } + + // As a really bad hack, accept writes that don't change the on disk + // content. This is needed because dbm doesn't keep track of dirty bits + // and can end up writing the same data to disk twice. Once when the + // user (nss) asks it to sync and once when closing the database. + auto wbuf2 = MakeUniqueFallible<char[]>(aCount); + if (!wbuf2) { + return true; + } + off_t pos = lseek(aFd, 0, SEEK_CUR); + if (pos == -1) { + return true; + } + ssize_t r = read(aFd, wbuf2.get(), aCount); + if (r < 0 || (size_t)r != aCount) { + return true; + } + int cmp = memcmp(aWbuf, wbuf2.get(), aCount); + if (cmp != 0) { + return true; + } + off_t pos2 = lseek(aFd, pos, SEEK_SET); + if (pos2 != pos) { + return true; + } + + // Otherwise this is not a valid write + return false; +} + +/*************************** Function Interception ***************************/ + +/** Structure for declaration of function override */ +struct FuncData +{ + const char* Name; // Name of the function for the ones we use dlsym + const void* Wrapper; // The function that we will replace 'Function' with + void* Function; // The function that will be replaced with 'Wrapper' + void* Buffer; // Will point to the jump buffer that lets us call + // 'Function' after it has been replaced. +}; + +// Wrap aio_write. We have not seen it before, so just assert/report it. +typedef ssize_t (*aio_write_t)(struct aiocb* aAioCbp); +ssize_t wrap_aio_write(struct aiocb* aAioCbp); +FuncData aio_write_data = { 0, (void*)wrap_aio_write, (void*)aio_write }; +ssize_t +wrap_aio_write(struct aiocb* aAioCbp) +{ + MacIOAutoObservation timer(IOInterposeObserver::OpWrite, + aAioCbp->aio_fildes); + + aio_write_t old_write = (aio_write_t)aio_write_data.Buffer; + return old_write(aAioCbp); +} + +// Wrap pwrite-like functions. +// We have not seen them before, so just assert/report it. +typedef ssize_t (*pwrite_t)(int aFd, const void* buf, size_t aNumBytes, + off_t aOffset); +template<FuncData& foo> +ssize_t +wrap_pwrite_temp(int aFd, const void* aBuf, size_t aNumBytes, off_t aOffset) +{ + MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd); + pwrite_t old_write = (pwrite_t)foo.Buffer; + return old_write(aFd, aBuf, aNumBytes, aOffset); +} + +// Define a FuncData for a pwrite-like functions. +#define DEFINE_PWRITE_DATA(X, NAME) \ +FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> }; \ + +// This exists everywhere. +DEFINE_PWRITE_DATA(pwrite, "pwrite") +// These exist on 32 bit OS X +DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003"); +DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003"); +// This exists on 64 bit OS X +DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL"); + + +typedef ssize_t (*writev_t)(int aFd, const struct iovec* aIov, int aIovCount); +template<FuncData& foo> +ssize_t +wrap_writev_temp(int aFd, const struct iovec* aIov, int aIovCount) +{ + MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd, nullptr, + aIovCount); + writev_t old_write = (writev_t)foo.Buffer; + return old_write(aFd, aIov, aIovCount); +} + +// Define a FuncData for a writev-like functions. +#define DEFINE_WRITEV_DATA(X, NAME) \ +FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \ + +// This exists everywhere. +DEFINE_WRITEV_DATA(writev, "writev"); +// These exist on 32 bit OS X +DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003"); +DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003"); +// This exists on 64 bit OS X +DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL"); + +typedef ssize_t (*write_t)(int aFd, const void* aBuf, size_t aCount); +template<FuncData& foo> +ssize_t +wrap_write_temp(int aFd, const void* aBuf, size_t aCount) +{ + MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd, aBuf, aCount); + write_t old_write = (write_t)foo.Buffer; + return old_write(aFd, aBuf, aCount); +} + +// Define a FuncData for a write-like functions. +#define DEFINE_WRITE_DATA(X, NAME) \ +FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \ + +// This exists everywhere. +DEFINE_WRITE_DATA(write, "write"); +// These exist on 32 bit OS X +DEFINE_WRITE_DATA(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003"); +DEFINE_WRITE_DATA(write_UNIX2003, "write$UNIX2003"); +// This exists on 64 bit OS X +DEFINE_WRITE_DATA(write_NOCANCEL, "write$NOCANCEL"); + +FuncData* Functions[] = { + &aio_write_data, + + &pwrite_data, + &pwrite_NOCANCEL_UNIX2003_data, + &pwrite_UNIX2003_data, + &pwrite_NOCANCEL_data, + + &write_data, + &write_NOCANCEL_UNIX2003_data, + &write_UNIX2003_data, + &write_NOCANCEL_data, + + &writev_data, + &writev_NOCANCEL_UNIX2003_data, + &writev_UNIX2003_data, + &writev_NOCANCEL_data +}; + +const int NumFunctions = ArrayLength(Functions); + +} // namespace + +/******************************** IO Poisoning ********************************/ + +namespace mozilla { + +void +InitPoisonIOInterposer() +{ + // Enable reporting from poisoned write methods + sIsEnabled = true; + + // Make sure we only poison writes once! + static bool WritesArePoisoned = false; + if (WritesArePoisoned) { + return; + } + WritesArePoisoned = true; + + // stdout and stderr are OK. + MozillaRegisterDebugFD(1); + MozillaRegisterDebugFD(2); + +#ifdef MOZ_REPLACE_MALLOC + // The contract with InitDebugFd is that the given registry can be used + // at any moment, so the instance needs to persist longer than the scope + // of this functions. + static DebugFdRegistry registry; + ReplaceMalloc::InitDebugFd(registry); +#endif + + for (int i = 0; i < NumFunctions; ++i) { + FuncData* d = Functions[i]; + if (!d->Function) { + d->Function = dlsym(RTLD_DEFAULT, d->Name); + } + if (!d->Function) { + continue; + } + DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper, + &d->Buffer); + MOZ_ASSERT(t == err_none); + } +} + +void +OnlyReportDirtyWrites() +{ + sOnlyReportDirtyWrites = true; +} + +void +ClearPoisonIOInterposer() +{ + // Not sure how or if we can unpoison the functions. Would be nice, but no + // worries we won't need to do this anyway. + sIsEnabled = false; +} + +} // namespace mozilla diff --git a/xpcom/build/PoisonIOInterposerStub.cpp b/xpcom/build/PoisonIOInterposerStub.cpp new file mode 100644 index 000000000..1f3daa353 --- /dev/null +++ b/xpcom/build/PoisonIOInterposerStub.cpp @@ -0,0 +1,16 @@ +/* -*- 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 <stdio.h> + +extern "C" { + +void MozillaRegisterDebugFD(int aFd) {} +void MozillaRegisterDebugFILE(FILE* aFile) {} +void MozillaUnRegisterDebugFD(int aFd) {} +void MozillaUnRegisterDebugFILE(FILE* aFile) {} + +} // extern "C" diff --git a/xpcom/build/PoisonIOInterposerWin.cpp b/xpcom/build/PoisonIOInterposerWin.cpp new file mode 100644 index 000000000..752743b34 --- /dev/null +++ b/xpcom/build/PoisonIOInterposerWin.cpp @@ -0,0 +1,501 @@ +/* -*- 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 "PoisonIOInterposer.h" + +#include <algorithm> +#include <stdio.h> +#include <vector> + +#include <io.h> +#include <windows.h> +#include <winternl.h> + +#include "mozilla/Assertions.h" +#include "mozilla/FileUtilsWin.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/Mutex.h" +#include "mozilla/TimeStamp.h" +#include "nsTArray.h" +#include "nsWindowsDllInterceptor.h" +#include "plstr.h" + +#ifdef MOZ_REPLACE_MALLOC +#include "replace_malloc_bridge.h" +#endif + +using namespace mozilla; + +namespace { + +// Keep track of poisoned state. Notice that there is no reason to lock access +// to this variable as it's only changed in InitPoisonIOInterposer and +// ClearPoisonIOInterposer which may only be called on the main-thread when no +// other threads are running. +static bool sIOPoisoned = false; + +/************************ Internal NT API Declarations ************************/ + +/* + * Function pointer declaration for internal NT routine to create/open files. + * For documentation on the NtCreateFile routine, see MSDN. + */ +typedef NTSTATUS (NTAPI* NtCreateFileFn)( + PHANDLE aFileHandle, + ACCESS_MASK aDesiredAccess, + POBJECT_ATTRIBUTES aObjectAttributes, + PIO_STATUS_BLOCK aIoStatusBlock, + PLARGE_INTEGER aAllocationSize, + ULONG aFileAttributes, + ULONG aShareAccess, + ULONG aCreateDisposition, + ULONG aCreateOptions, + PVOID aEaBuffer, + ULONG aEaLength); + +/** + * Function pointer declaration for internal NT routine to read data from file. + * For documentation on the NtReadFile routine, see ZwReadFile on MSDN. + */ +typedef NTSTATUS (NTAPI* NtReadFileFn)( + HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + PVOID aBuffer, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey); + +/** + * Function pointer declaration for internal NT routine to read data from file. + * No documentation exists, see wine sources for details. + */ +typedef NTSTATUS (NTAPI* NtReadFileScatterFn)( + HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + FILE_SEGMENT_ELEMENT* aSegments, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey); + +/** + * Function pointer declaration for internal NT routine to write data to file. + * For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN. + */ +typedef NTSTATUS (NTAPI* NtWriteFileFn)( + HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + PVOID aBuffer, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey); + +/** + * Function pointer declaration for internal NT routine to write data to file. + * No documentation exists, see wine sources for details. + */ +typedef NTSTATUS (NTAPI* NtWriteFileGatherFn)( + HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + FILE_SEGMENT_ELEMENT* aSegments, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey); + +/** + * Function pointer declaration for internal NT routine to flush to disk. + * For documentation on the NtFlushBuffersFile routine, see ZwFlushBuffersFile + * on MSDN. + */ +typedef NTSTATUS (NTAPI* NtFlushBuffersFileFn)( + HANDLE aFileHandle, + PIO_STATUS_BLOCK aIoStatusBlock); + +typedef struct _FILE_NETWORK_OPEN_INFORMATION* PFILE_NETWORK_OPEN_INFORMATION; +/** + * Function pointer delaration for internal NT routine to query file attributes. + * (equivalent to stat) + */ +typedef NTSTATUS (NTAPI* NtQueryFullAttributesFileFn)( + POBJECT_ATTRIBUTES aObjectAttributes, + PFILE_NETWORK_OPEN_INFORMATION aFileInformation); + +/*************************** Auxiliary Declarations ***************************/ + +/** + * RAII class for timing the duration of an I/O call and reporting the result + * to the IOInterposeObserver API. + */ +class WinIOAutoObservation : public IOInterposeObserver::Observation +{ +public: + WinIOAutoObservation(IOInterposeObserver::Operation aOp, + HANDLE aFileHandle, const LARGE_INTEGER* aOffset) + : IOInterposeObserver::Observation( + aOp, sReference, !IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle))) + , mFileHandle(aFileHandle) + , mHasQueriedFilename(false) + , mFilename(nullptr) + { + if (mShouldReport) { + mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0; + } + } + + WinIOAutoObservation(IOInterposeObserver::Operation aOp, nsAString& aFilename) + : IOInterposeObserver::Observation(aOp, sReference) + , mFileHandle(nullptr) + , mHasQueriedFilename(false) + , mFilename(nullptr) + { + if (mShouldReport) { + nsAutoString dosPath; + if (NtPathToDosPath(aFilename, dosPath)) { + mFilename = ToNewUnicode(dosPath); + mHasQueriedFilename = true; + } + mOffset.QuadPart = 0; + } + } + + // Custom implementation of IOInterposeObserver::Observation::Filename + const char16_t* Filename() override; + + ~WinIOAutoObservation() + { + Report(); + if (mFilename) { + MOZ_ASSERT(mHasQueriedFilename); + free(mFilename); + mFilename = nullptr; + } + } + +private: + HANDLE mFileHandle; + LARGE_INTEGER mOffset; + bool mHasQueriedFilename; + char16_t* mFilename; + static const char* sReference; +}; + +const char* WinIOAutoObservation::sReference = "PoisonIOInterposer"; + +// Get filename for this observation +const char16_t* +WinIOAutoObservation::Filename() +{ + // If mHasQueriedFilename is true, then filename is already stored in mFilename + if (mHasQueriedFilename) { + return mFilename; + } + + nsAutoString utf16Filename; + if (HandleToFilename(mFileHandle, mOffset, utf16Filename)) { + // Heap allocate with leakable memory + mFilename = ToNewUnicode(utf16Filename); + } + mHasQueriedFilename = true; + + // Return filename + return mFilename; +} + +/*************************** IO Interposing Methods ***************************/ + +// Function pointers to original functions +static NtCreateFileFn gOriginalNtCreateFile; +static NtReadFileFn gOriginalNtReadFile; +static NtReadFileScatterFn gOriginalNtReadFileScatter; +static NtWriteFileFn gOriginalNtWriteFile; +static NtWriteFileGatherFn gOriginalNtWriteFileGather; +static NtFlushBuffersFileFn gOriginalNtFlushBuffersFile; +static NtQueryFullAttributesFileFn gOriginalNtQueryFullAttributesFile; + +static NTSTATUS NTAPI +InterposedNtCreateFile(PHANDLE aFileHandle, + ACCESS_MASK aDesiredAccess, + POBJECT_ATTRIBUTES aObjectAttributes, + PIO_STATUS_BLOCK aIoStatusBlock, + PLARGE_INTEGER aAllocationSize, + ULONG aFileAttributes, + ULONG aShareAccess, + ULONG aCreateDisposition, + ULONG aCreateOptions, + PVOID aEaBuffer, + ULONG aEaLength) +{ + // Report IO + const wchar_t* buf = + aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L""; + uint32_t len = + aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) : + 0; + nsDependentSubstring filename(buf, len); + WinIOAutoObservation timer(IOInterposeObserver::OpCreateOrOpen, filename); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtCreateFile); + + // Execute original function + return gOriginalNtCreateFile(aFileHandle, + aDesiredAccess, + aObjectAttributes, + aIoStatusBlock, + aAllocationSize, + aFileAttributes, + aShareAccess, + aCreateDisposition, + aCreateOptions, + aEaBuffer, + aEaLength); +} + +static NTSTATUS NTAPI +InterposedNtReadFile(HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + PVOID aBuffer, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey) +{ + // Report IO + WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtReadFile); + + // Execute original function + return gOriginalNtReadFile(aFileHandle, + aEvent, + aApc, + aApcCtx, + aIoStatus, + aBuffer, + aLength, + aOffset, + aKey); +} + +static NTSTATUS NTAPI +InterposedNtReadFileScatter(HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + FILE_SEGMENT_ELEMENT* aSegments, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey) +{ + // Report IO + WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtReadFileScatter); + + // Execute original function + return gOriginalNtReadFileScatter(aFileHandle, + aEvent, + aApc, + aApcCtx, + aIoStatus, + aSegments, + aLength, + aOffset, + aKey); +} + +// Interposed NtWriteFile function +static NTSTATUS NTAPI +InterposedNtWriteFile(HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + PVOID aBuffer, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey) +{ + // Report IO + WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle, + aOffset); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtWriteFile); + + // Execute original function + return gOriginalNtWriteFile(aFileHandle, + aEvent, + aApc, + aApcCtx, + aIoStatus, + aBuffer, + aLength, + aOffset, + aKey); +} + +// Interposed NtWriteFileGather function +static NTSTATUS NTAPI +InterposedNtWriteFileGather(HANDLE aFileHandle, + HANDLE aEvent, + PIO_APC_ROUTINE aApc, + PVOID aApcCtx, + PIO_STATUS_BLOCK aIoStatus, + FILE_SEGMENT_ELEMENT* aSegments, + ULONG aLength, + PLARGE_INTEGER aOffset, + PULONG aKey) +{ + // Report IO + WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle, + aOffset); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtWriteFileGather); + + // Execute original function + return gOriginalNtWriteFileGather(aFileHandle, + aEvent, + aApc, + aApcCtx, + aIoStatus, + aSegments, + aLength, + aOffset, + aKey); +} + +static NTSTATUS NTAPI +InterposedNtFlushBuffersFile(HANDLE aFileHandle, + PIO_STATUS_BLOCK aIoStatusBlock) +{ + // Report IO + WinIOAutoObservation timer(IOInterposeObserver::OpFSync, aFileHandle, + nullptr); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtFlushBuffersFile); + + // Execute original function + return gOriginalNtFlushBuffersFile(aFileHandle, + aIoStatusBlock); +} + +static NTSTATUS NTAPI +InterposedNtQueryFullAttributesFile( + POBJECT_ATTRIBUTES aObjectAttributes, + PFILE_NETWORK_OPEN_INFORMATION aFileInformation) +{ + // Report IO + const wchar_t* buf = + aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L""; + uint32_t len = + aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) : + 0; + nsDependentSubstring filename(buf, len); + WinIOAutoObservation timer(IOInterposeObserver::OpStat, filename); + + // Something is badly wrong if this function is undefined + MOZ_ASSERT(gOriginalNtQueryFullAttributesFile); + + // Execute original function + return gOriginalNtQueryFullAttributesFile(aObjectAttributes, + aFileInformation); +} + +} // namespace + +/******************************** IO Poisoning ********************************/ + +// Windows DLL interceptor +static WindowsDllInterceptor sNtDllInterceptor; + +namespace mozilla { + +void +InitPoisonIOInterposer() +{ + // Don't poison twice... as this function may only be invoked on the main + // thread when no other threads are running, it safe to allow multiple calls + // to InitPoisonIOInterposer() without complaining (ie. failing assertions). + if (sIOPoisoned) { + return; + } + sIOPoisoned = true; + + // Stdout and Stderr are OK. + MozillaRegisterDebugFD(1); + MozillaRegisterDebugFD(2); + +#ifdef MOZ_REPLACE_MALLOC + // The contract with InitDebugFd is that the given registry can be used + // at any moment, so the instance needs to persist longer than the scope + // of this functions. + static DebugFdRegistry registry; + ReplaceMalloc::InitDebugFd(registry); +#endif + + // Initialize dll interceptor and add hooks + sNtDllInterceptor.Init("ntdll.dll"); + sNtDllInterceptor.AddHook( + "NtCreateFile", + reinterpret_cast<intptr_t>(InterposedNtCreateFile), + reinterpret_cast<void**>(&gOriginalNtCreateFile)); + sNtDllInterceptor.AddHook( + "NtReadFile", + reinterpret_cast<intptr_t>(InterposedNtReadFile), + reinterpret_cast<void**>(&gOriginalNtReadFile)); + sNtDllInterceptor.AddHook( + "NtReadFileScatter", + reinterpret_cast<intptr_t>(InterposedNtReadFileScatter), + reinterpret_cast<void**>(&gOriginalNtReadFileScatter)); + sNtDllInterceptor.AddHook( + "NtWriteFile", + reinterpret_cast<intptr_t>(InterposedNtWriteFile), + reinterpret_cast<void**>(&gOriginalNtWriteFile)); + sNtDllInterceptor.AddHook( + "NtWriteFileGather", + reinterpret_cast<intptr_t>(InterposedNtWriteFileGather), + reinterpret_cast<void**>(&gOriginalNtWriteFileGather)); + sNtDllInterceptor.AddHook( + "NtFlushBuffersFile", + reinterpret_cast<intptr_t>(InterposedNtFlushBuffersFile), + reinterpret_cast<void**>(&gOriginalNtFlushBuffersFile)); + sNtDllInterceptor.AddHook( + "NtQueryFullAttributesFile", + reinterpret_cast<intptr_t>(InterposedNtQueryFullAttributesFile), + reinterpret_cast<void**>(&gOriginalNtQueryFullAttributesFile)); +} + +void +ClearPoisonIOInterposer() +{ + MOZ_ASSERT(false); + if (sIOPoisoned) { + // Destroy the DLL interceptor + sIOPoisoned = false; + sNtDllInterceptor = WindowsDllInterceptor(); + } +} + +} // namespace mozilla diff --git a/xpcom/build/ServiceList.h b/xpcom/build/ServiceList.h new file mode 100644 index 000000000..3bb8308dd --- /dev/null +++ b/xpcom/build/ServiceList.h @@ -0,0 +1,46 @@ +/* -*- 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/. */ +// IWYU pragma: private, include "mozilla/Services.h" + +MOZ_SERVICE(ChromeRegistryService, nsIChromeRegistry, + "@mozilla.org/chrome/chrome-registry;1") +MOZ_SERVICE(ToolkitChromeRegistryService, nsIToolkitChromeRegistry, + "@mozilla.org/chrome/chrome-registry;1") +MOZ_SERVICE(XULChromeRegistryService, nsIXULChromeRegistry, + "@mozilla.org/chrome/chrome-registry;1") +MOZ_SERVICE(XULOverlayProviderService, nsIXULOverlayProvider, + "@mozilla.org/chrome/chrome-registry;1") +MOZ_SERVICE(IOService, nsIIOService, + "@mozilla.org/network/io-service;1") +MOZ_SERVICE(ObserverService, nsIObserverService, + "@mozilla.org/observer-service;1") +MOZ_SERVICE(StringBundleService, nsIStringBundleService, + "@mozilla.org/intl/stringbundle;1") +MOZ_SERVICE(XPConnect, nsIXPConnect, + "@mozilla.org/js/xpc/XPConnect;1") +MOZ_SERVICE(InDOMUtils, inIDOMUtils, + "@mozilla.org/inspector/dom-utils;1") +MOZ_SERVICE(PermissionManager, nsIPermissionManager, + "@mozilla.org/permissionmanager;1"); +MOZ_SERVICE(ServiceWorkerManager, nsIServiceWorkerManager, + "@mozilla.org/serviceworkers/manager;1"); +MOZ_SERVICE(AsyncShutdown, nsIAsyncShutdownService, + "@mozilla.org/async-shutdown-service;1") +MOZ_SERVICE(UUIDGenerator, nsIUUIDGenerator, + "@mozilla.org/uuid-generator;1"); +MOZ_SERVICE(GfxInfo, nsIGfxInfo, + "@mozilla.org/gfx/info;1"); + +#ifdef MOZ_USE_NAMESPACE +namespace mozilla { +#endif + +MOZ_SERVICE(HistoryService, IHistory, + "@mozilla.org/browser/history;1") + +#ifdef MOZ_USE_NAMESPACE +} // namespace mozilla +#endif diff --git a/xpcom/build/Services.cpp b/xpcom/build/Services.cpp new file mode 100644 index 000000000..6c521391e --- /dev/null +++ b/xpcom/build/Services.cpp @@ -0,0 +1,71 @@ +/* -*- 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 "mozilla/Likely.h" +#include "mozilla/Services.h" +#include "nsComponentManager.h" +#include "nsIObserverService.h" +#include "nsNetCID.h" +#include "nsObserverService.h" +#include "nsXPCOMPrivate.h" +#include "nsIIOService.h" +#include "nsIDirectoryService.h" +#include "nsIChromeRegistry.h" +#include "nsIStringBundle.h" +#include "nsIToolkitChromeRegistry.h" +#include "nsIXULOverlayProvider.h" +#include "IHistory.h" +#include "nsIXPConnect.h" +#include "inIDOMUtils.h" +#include "nsIPermissionManager.h" +#include "nsIServiceWorkerManager.h" +#include "nsIAsyncShutdown.h" +#include "nsIUUIDGenerator.h" +#include "nsIGfxInfo.h" + +using namespace mozilla; +using namespace mozilla::services; + +/* + * Define a global variable and a getter for every service in ServiceList. + * eg. gIOService and GetIOService() + */ +#define MOZ_SERVICE(NAME, TYPE, CONTRACT_ID) \ + static TYPE* g##NAME = nullptr; \ + \ + already_AddRefed<TYPE> \ + mozilla::services::Get##NAME() \ + { \ + if (MOZ_UNLIKELY(gXPCOMShuttingDown)) { \ + return nullptr; \ + } \ + if (!g##NAME) { \ + nsCOMPtr<TYPE> os = do_GetService(CONTRACT_ID); \ + os.swap(g##NAME); \ + } \ + nsCOMPtr<TYPE> ret = g##NAME; \ + return ret.forget(); \ + } \ + NS_EXPORT_(already_AddRefed<TYPE>) \ + mozilla::services::_external_Get##NAME() \ + { \ + return Get##NAME(); \ + } + +#include "ServiceList.h" +#undef MOZ_SERVICE + +/** + * Clears service cache, sets gXPCOMShuttingDown + */ +void +mozilla::services::Shutdown() +{ + gXPCOMShuttingDown = true; +#define MOZ_SERVICE(NAME, TYPE, CONTRACT_ID) NS_IF_RELEASE(g##NAME); +#include "ServiceList.h" +#undef MOZ_SERVICE +} diff --git a/xpcom/build/Services.h b/xpcom/build/Services.h new file mode 100644 index 000000000..84a355399 --- /dev/null +++ b/xpcom/build/Services.h @@ -0,0 +1,45 @@ +/* -*- 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/. */ + +#ifndef mozilla_Services_h +#define mozilla_Services_h + +#include "nscore.h" +#include "nsCOMPtr.h" + +#define MOZ_USE_NAMESPACE +#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) class TYPE; + +#include "ServiceList.h" +#undef MOZ_SERVICE +#undef MOZ_USE_NAMESPACE + +namespace mozilla { +namespace services { + +#ifdef MOZILLA_INTERNAL_API +#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) \ + already_AddRefed<TYPE> Get##NAME(); \ + NS_EXPORT_(already_AddRefed<TYPE>) _external_Get##NAME(); + +#include "ServiceList.h" +#undef MOZ_SERVICE +#else +#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) \ + NS_IMPORT_(already_AddRefed<TYPE>) _external_Get##NAME(); \ + inline already_AddRefed<TYPE> Get##NAME() \ + { \ + return _external_Get##NAME(); \ + } + +#include "ServiceList.h" +#undef MOZ_SERVICE +#endif + +} // namespace services +} // namespace mozilla + +#endif diff --git a/xpcom/build/XPCOM.h b/xpcom/build/XPCOM.h new file mode 100644 index 000000000..f52a87c69 --- /dev/null +++ b/xpcom/build/XPCOM.h @@ -0,0 +1,178 @@ +/* -*- 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/. */ + +#ifndef mozilla_XPCOM_h +#define mozilla_XPCOM_h + +// NOTE: the following headers are sorted topologically, not alphabetically. +// Do not reorder them without review from bsmedberg. + +// system headers required by XPCOM headers + +#include <string.h> + +// core headers required by pretty much everything else + +#include "nscore.h" + +#include "nsXPCOMCID.h" +#include "nsXPCOM.h" + +#include "nsError.h" +#include "nsDebug.h" +#include "nsMemory.h" + +#include "nsID.h" + +#include "nsISupports.h" + +#include "nsTArray.h" +#include "nsTWeakRef.h" + +#include "nsCOMPtr.h" +#include "nsCOMArray.h" + +#ifndef MOZILLA_INTERNAL_API +#include "nsStringAPI.h" +#else +#include "nsString.h" +#include "nsReadableUtils.h" +#include "nsNativeCharsetUtils.h" +#endif + +#include "nsISupportsUtils.h" +#include "nsISupportsImpl.h" + +// core data structures + +#include "nsTHashtable.h" +#include "nsHashKeys.h" +#include "nsBaseHashtable.h" +#include "nsDataHashtable.h" +#include "nsInterfaceHashtable.h" +#include "nsClassHashtable.h" +#include "nsRefPtrHashtable.h" + +// interfaces that inherit directly from nsISupports + +#include "nsIArray.h" +#include "nsIAtom.h" +#include "nsIAtomService.h" +#include "nsICategoryManager.h" +#include "nsIClassInfo.h" +#include "nsIComponentManager.h" +#include "nsIConsoleListener.h" +#include "nsIConsoleMessage.h" +#include "nsIConsoleService.h" +#include "nsIDebug2.h" +#include "nsIDirectoryEnumerator.h" +#include "nsIEnvironment.h" +#include "nsIErrorService.h" +#include "nsIEventTarget.h" +#include "nsIException.h" +#include "nsIFactory.h" +#include "nsIFile.h" +#include "nsIHashable.h" +#include "nsIINIParser.h" +#include "nsIInputStream.h" +#include "nsIInterfaceRequestor.h" +#include "nsILineInputStream.h" +#include "nsIMemory.h" +#include "nsIMutable.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsIOutputStream.h" +#include "nsIProcess.h" +#include "nsIProperties.h" +#include "nsIPropertyBag2.h" +#include "nsIRunnable.h" +#include "nsISeekableStream.h" +#include "nsISerializable.h" +#include "nsIServiceManager.h" +#include "nsIScriptableInputStream.h" +#include "nsISimpleEnumerator.h" +#include "nsIStreamBufferAccess.h" +#include "nsIStringEnumerator.h" +#include "nsIStorageStream.h" +#include "nsISupportsIterators.h" +#include "nsISupportsPrimitives.h" +#include "nsISupportsPriority.h" +#include "nsIThreadManager.h" +#include "nsITimer.h" +#include "nsIUUIDGenerator.h" +#include "nsIUnicharInputStream.h" +#include "nsIUnicharOutputStream.h" +#include "nsIUnicharLineInputStream.h" +#include "nsIVariant.h" +#include "nsIVersionComparator.h" +#include "nsIWritablePropertyBag2.h" + +// interfaces that include something above + +#include "nsIAsyncInputStream.h" +#include "nsIAsyncOutputStream.h" +#include "nsIBinaryInputStream.h" +#include "nsIBinaryOutputStream.h" +#include "nsIConverterInputStream.h" +#include "nsIConverterOutputStream.h" +#include "nsIInputStreamTee.h" +#include "nsIMultiplexInputStream.h" +#include "nsIMutableArray.h" +#include "nsIPersistentProperties2.h" +#include "nsIStringStream.h" +#include "nsIThread.h" +#include "nsIThreadPool.h" + +// interfaces that include something above + +#include "nsILocalFileWin.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsIPipe.h" + +#ifdef MOZ_WIDGET_COCOA +#include "nsILocalFileMac.h" +#include "nsIMacUtils.h" +#endif + +// xpcom/glue utility headers + +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" + +#include "nsIWeakReferenceUtils.h" +#include "nsWeakReference.h" + +#include "nsArrayEnumerator.h" +#include "nsArrayUtils.h" +#include "nsCRTGlue.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDeque.h" +#include "nsEnumeratorUtils.h" +#include "nsIClassInfoImpl.h" +#include "mozilla/ModuleUtils.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsINIParser.h" +#include "nsProxyRelease.h" +#include "nsTObserverArray.h" +#include "nsTextFormatter.h" +#include "nsThreadUtils.h" +#include "nsVersionComparator.h" +#include "nsXPTCUtils.h" + +// xpcom/base utility headers + +#include "nsAgg.h" +#include "nsAutoRef.h" +#include "nsInterfaceRequestorAgg.h" + +// xpcom/io utility headers + +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" + +#endif // mozilla_XPCOM_h diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp new file mode 100644 index 000000000..c72ea48d7 --- /dev/null +++ b/xpcom/build/XPCOMInit.cpp @@ -0,0 +1,1126 @@ +/* -*- 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 "base/basictypes.h" + +#include "mozilla/AbstractThread.h" +#include "mozilla/Atomics.h" +#include "mozilla/Poison.h" +#include "mozilla/SharedThreadPool.h" +#include "mozilla/XPCOM.h" +#include "nsXULAppAPI.h" + +#include "nsXPCOMPrivate.h" +#include "nsXPCOMCIDInternal.h" + +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/dom/VideoDecoderManagerChild.h" + +#include "prlink.h" + +#include "nsCycleCollector.h" +#include "nsObserverList.h" +#include "nsObserverService.h" +#include "nsProperties.h" +#include "nsPersistentProperties.h" +#include "nsScriptableInputStream.h" +#include "nsBinaryStream.h" +#include "nsStorageStream.h" +#include "nsPipe.h" +#include "nsScriptableBase64Encoder.h" + +#include "nsMemoryImpl.h" +#include "nsDebugImpl.h" +#include "nsTraceRefcnt.h" +#include "nsErrorService.h" + +// Disable deprecation warnings generated by nsISupportsArray and associated +// classes. +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable : 4996) +#endif + +#include "nsSupportsArray.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning (pop) +#endif + +#include "nsArray.h" +#include "nsINIParserImpl.h" +#include "nsSupportsPrimitives.h" +#include "nsConsoleService.h" + +#include "nsComponentManager.h" +#include "nsCategoryManagerUtils.h" +#include "nsIServiceManager.h" + +#include "nsThreadManager.h" +#include "nsThreadPool.h" + +#include "xptinfo.h" +#include "nsIInterfaceInfoManager.h" +#include "xptiprivate.h" +#include "mozilla/XPTInterfaceInfoManager.h" + +#include "nsTimerImpl.h" +#include "TimerThread.h" + +#include "nsThread.h" +#include "nsProcess.h" +#include "nsEnvironment.h" +#include "nsVersionComparatorImpl.h" + +#include "nsIFile.h" +#include "nsLocalFile.h" +#if defined(XP_UNIX) +#include "nsNativeCharsetUtils.h" +#endif +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsCategoryManager.h" +#include "nsICategoryManager.h" +#include "nsMultiplexInputStream.h" + +#include "nsStringStream.h" +extern nsresult nsStringInputStreamConstructor(nsISupports*, REFNSIID, void**); + +#include "nsAtomService.h" +#include "nsAtomTable.h" +#include "nsISupportsImpl.h" + +#include "nsHashPropertyBag.h" + +#include "nsUnicharInputStream.h" +#include "nsVariant.h" + +#include "nsUUIDGenerator.h" + +#include "nsIOUtil.h" + +#include "SpecialSystemDirectory.h" + +#if defined(XP_WIN) +#include "mozilla/WindowsVersion.h" +#include "nsWindowsRegKey.h" +#endif + +#ifdef MOZ_WIDGET_COCOA +#include "nsMacUtilsImpl.h" +#endif + +#include "nsSystemInfo.h" +#include "nsMemoryReporterManager.h" +#include "nsMemoryInfoDumper.h" +#include "nsSecurityConsoleMessage.h" +#include "nsMessageLoop.h" + +#include "nsStatusReporterManager.h" + +#include <locale.h> +#include "mozilla/Services.h" +#include "mozilla/Omnijar.h" +#include "mozilla/HangMonitor.h" +#include "mozilla/Telemetry.h" +#include "mozilla/BackgroundHangMonitor.h" + +#include "nsChromeRegistry.h" +#include "nsChromeProtocolHandler.h" +#include "mozilla/PoisonIOInterposer.h" +#include "mozilla/LateWriteChecks.h" + +#include "mozilla/scache/StartupCache.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/message_loop.h" + +#include "mozilla/ipc/BrowserProcessSubThread.h" +#include "mozilla/AvailableMemoryTracker.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/CountingAllocatorBase.h" +#include "mozilla/SystemMemoryReporter.h" +#include "mozilla/UniquePtr.h" + +#include "mozilla/ipc/GeckoChildProcessHost.h" + +#include "ogg/ogg.h" +#if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING) +#if defined(HAVE_STDINT_H) +// mozilla-config.h defines HAVE_STDINT_H, and then it's defined *again* in +// vpx_config.h (which we include via vpx_mem.h, below). This redefinition +// triggers a build warning on MSVC, so we have to #undef it first. +#undef HAVE_STDINT_H +#endif +#include "vpx_mem/vpx_mem.h" +#endif + +#include "GeckoProfiler.h" + +#include "jsapi.h" +#include "js/Initialization.h" + +#include "gfxPlatform.h" + +#if EXPOSE_INTL_API +#include "unicode/putil.h" +#endif + +using namespace mozilla; +using base::AtExitManager; +using mozilla::ipc::BrowserProcessSubThread; + +namespace { + +static AtExitManager* sExitManager; +static MessageLoop* sMessageLoop; +static bool sCommandLineWasInitialized; +static BrowserProcessSubThread* sIOThread; +static BackgroundHangMonitor* sMainHangMonitor; + +} /* anonymous namespace */ + +// Registry Factory creation function defined in nsRegistry.cpp +// We hook into this function locally to create and register the registry +// Since noone outside xpcom needs to know about this and nsRegistry.cpp +// does not have a local include file, we are putting this definition +// here rather than in nsIRegistry.h +extern nsresult NS_RegistryGetFactory(nsIFactory** aFactory); +extern nsresult NS_CategoryManagerGetFactory(nsIFactory**); + +#ifdef XP_WIN +extern nsresult CreateAnonTempFileRemover(); +#endif + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsProcess) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsID) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsString) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsCString) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRBool) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint8) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint16) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint32) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint64) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRTime) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsChar) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt16) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt32) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt64) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsFloat) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsDouble) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsVoid) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsInterfacePointer) + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsConsoleService, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsAtomService) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsTimer) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryOutputStream) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryInputStream) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsStorageStream) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsVersionComparatorImpl) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableBase64Encoder) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsVariantCC) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsHashPropertyBagCC) + +NS_GENERIC_AGGREGATED_CONSTRUCTOR(nsProperties) + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUUIDGenerator, Init) + +#ifdef MOZ_WIDGET_COCOA +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacUtilsImpl) +#endif + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemInfo, Init) + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemoryReporterManager, Init) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMemoryInfoDumper) + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStatusReporterManager, Init) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsIOUtil) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecurityConsoleMessage) + +static nsresult +nsThreadManagerGetSingleton(nsISupports* aOuter, + const nsIID& aIID, + void** aInstancePtr) +{ + NS_ASSERTION(aInstancePtr, "null outptr"); + if (NS_WARN_IF(aOuter)) { + return NS_ERROR_NO_AGGREGATION; + } + + return nsThreadManager::get().QueryInterface(aIID, aInstancePtr); +} + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThreadPool) + +static nsresult +nsXPTIInterfaceInfoManagerGetSingleton(nsISupports* aOuter, + const nsIID& aIID, + void** aInstancePtr) +{ + NS_ASSERTION(aInstancePtr, "null outptr"); + if (NS_WARN_IF(aOuter)) { + return NS_ERROR_NO_AGGREGATION; + } + + nsCOMPtr<nsIInterfaceInfoManager> iim(XPTInterfaceInfoManager::GetSingleton()); + if (!iim) { + return NS_ERROR_FAILURE; + } + + return iim->QueryInterface(aIID, aInstancePtr); +} + +nsComponentManagerImpl* nsComponentManagerImpl::gComponentManager = nullptr; +bool gXPCOMShuttingDown = false; +bool gXPCOMThreadsShutDown = false; +char16_t* gGREBinPath = nullptr; + +static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); +static NS_DEFINE_CID(kINIParserFactoryCID, NS_INIPARSERFACTORY_CID); + +NS_DEFINE_NAMED_CID(NS_CHROMEREGISTRY_CID); +NS_DEFINE_NAMED_CID(NS_CHROMEPROTOCOLHANDLER_CID); + +NS_DEFINE_NAMED_CID(NS_SECURITY_CONSOLE_MESSAGE_CID); + +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsChromeRegistry, + nsChromeRegistry::GetSingleton) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsChromeProtocolHandler) + +#define NS_PERSISTENTPROPERTIES_CID NS_IPERSISTENTPROPERTIES_CID /* sigh */ + +static already_AddRefed<nsIFactory> +CreateINIParserFactory(const mozilla::Module& aModule, + const mozilla::Module::CIDEntry& aEntry) +{ + nsCOMPtr<nsIFactory> f = new nsINIParserFactory(); + return f.forget(); +} + +#define COMPONENT(NAME, Ctor) static NS_DEFINE_CID(kNS_##NAME##_CID, NS_##NAME##_CID); +#define COMPONENT_M(NAME, Ctor, Selector) static NS_DEFINE_CID(kNS_##NAME##_CID, NS_##NAME##_CID); +#include "XPCOMModule.inc" +#undef COMPONENT +#undef COMPONENT_M + +#define COMPONENT(NAME, Ctor) { &kNS_##NAME##_CID, false, nullptr, Ctor }, +#define COMPONENT_M(NAME, Ctor, Selector) { &kNS_##NAME##_CID, false, nullptr, Ctor, Selector }, +const mozilla::Module::CIDEntry kXPCOMCIDEntries[] = { + { &kComponentManagerCID, true, nullptr, nsComponentManagerImpl::Create, Module::ALLOW_IN_GPU_PROCESS }, + { &kINIParserFactoryCID, false, CreateINIParserFactory }, +#include "XPCOMModule.inc" + { &kNS_CHROMEREGISTRY_CID, false, nullptr, nsChromeRegistryConstructor }, + { &kNS_CHROMEPROTOCOLHANDLER_CID, false, nullptr, nsChromeProtocolHandlerConstructor }, + { &kNS_SECURITY_CONSOLE_MESSAGE_CID, false, nullptr, nsSecurityConsoleMessageConstructor }, + { nullptr } +}; +#undef COMPONENT +#undef COMPONENT_M + +#define COMPONENT(NAME, Ctor) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID }, +#define COMPONENT_M(NAME, Ctor, Selector) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID, Selector }, +const mozilla::Module::ContractIDEntry kXPCOMContracts[] = { +#include "XPCOMModule.inc" + { NS_CHROMEREGISTRY_CONTRACTID, &kNS_CHROMEREGISTRY_CID }, + { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "chrome", &kNS_CHROMEPROTOCOLHANDLER_CID }, + { NS_INIPARSERFACTORY_CONTRACTID, &kINIParserFactoryCID }, + { NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &kNS_SECURITY_CONSOLE_MESSAGE_CID }, + { nullptr } +}; +#undef COMPONENT +#undef COMPONENT_M + +const mozilla::Module kXPCOMModule = { + mozilla::Module::kVersion, kXPCOMCIDEntries, kXPCOMContracts, + nullptr, + nullptr, + nullptr, + nullptr, + Module::ALLOW_IN_GPU_PROCESS +}; + +// gDebug will be freed during shutdown. +static nsIDebug2* gDebug = nullptr; + +EXPORT_XPCOM_API(nsresult) +NS_GetDebug(nsIDebug2** aResult) +{ + return nsDebugImpl::Create(nullptr, NS_GET_IID(nsIDebug2), (void**)aResult); +} + +EXPORT_XPCOM_API(nsresult) +NS_InitXPCOM(nsIServiceManager** aResult, + nsIFile* aBinDirectory) +{ + return NS_InitXPCOM2(aResult, aBinDirectory, nullptr); +} + +class ICUReporter final + : public nsIMemoryReporter + , public CountingAllocatorBase<ICUReporter> +{ +public: + NS_DECL_ISUPPORTS + + static void* Alloc(const void*, size_t aSize) + { + return CountingMalloc(aSize); + } + + static void* Realloc(const void*, void* aPtr, size_t aSize) + { + return CountingRealloc(aPtr, aSize); + } + + static void Free(const void*, void* aPtr) + { + return CountingFree(aPtr); + } + +private: + NS_IMETHOD + CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, + bool aAnonymize) override + { + MOZ_COLLECT_REPORT( + "explicit/icu", KIND_HEAP, UNITS_BYTES, MemoryAllocated(), + "Memory used by ICU, a Unicode and globalization support library."); + + return NS_OK; + } + + ~ICUReporter() {} +}; + +NS_IMPL_ISUPPORTS(ICUReporter, nsIMemoryReporter) + +/* static */ template<> Atomic<size_t> +CountingAllocatorBase<ICUReporter>::sAmount(0); + +class OggReporter final + : public nsIMemoryReporter + , public CountingAllocatorBase<OggReporter> +{ +public: + NS_DECL_ISUPPORTS + +private: + NS_IMETHOD + CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, + bool aAnonymize) override + { + MOZ_COLLECT_REPORT( + "explicit/media/libogg", KIND_HEAP, UNITS_BYTES, MemoryAllocated(), + "Memory allocated through libogg for Ogg, Theora, and related media " + "files."); + + return NS_OK; + } + + ~OggReporter() {} +}; + +NS_IMPL_ISUPPORTS(OggReporter, nsIMemoryReporter) + +/* static */ template<> Atomic<size_t> +CountingAllocatorBase<OggReporter>::sAmount(0); + +#ifdef MOZ_VPX +class VPXReporter final + : public nsIMemoryReporter + , public CountingAllocatorBase<VPXReporter> +{ +public: + NS_DECL_ISUPPORTS + +private: + NS_IMETHOD + CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, + bool aAnonymize) override + { + MOZ_COLLECT_REPORT( + "explicit/media/libvpx", KIND_HEAP, UNITS_BYTES, MemoryAllocated(), + "Memory allocated through libvpx for WebM media files."); + + return NS_OK; + } + + ~VPXReporter() {} +}; + +NS_IMPL_ISUPPORTS(VPXReporter, nsIMemoryReporter) + +/* static */ template<> Atomic<size_t> +CountingAllocatorBase<VPXReporter>::sAmount(0); +#endif /* MOZ_VPX */ + +static double +TimeSinceProcessCreation() +{ + bool ignore; + return (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds(); +} + +static bool sInitializedJS = false; + +// Note that on OSX, aBinDirectory will point to .app/Contents/Resources/browser +EXPORT_XPCOM_API(nsresult) +NS_InitXPCOM2(nsIServiceManager** aResult, + nsIFile* aBinDirectory, + nsIDirectoryServiceProvider* aAppFileLocationProvider) +{ + static bool sInitialized = false; + if (sInitialized) { + return NS_ERROR_FAILURE; + } + + sInitialized = true; + + mozPoisonValueInit(); + + NS_LogInit(); + + NS_InitAtomTable(); + + mozilla::LogModule::Init(); + + JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation); + + char aLocal; + profiler_init(&aLocal); + nsresult rv = NS_OK; + + // We are not shutting down + gXPCOMShuttingDown = false; + + // Initialize the available memory tracker before other threads have had a + // chance to start up, because the initialization is not thread-safe. + mozilla::AvailableMemoryTracker::Init(); + +#ifdef XP_UNIX + // Discover the current value of the umask, and save it where + // nsSystemInfo::Init can retrieve it when necessary. There is no way + // to read the umask without changing it, and the setting is process- + // global, so this must be done while we are still single-threaded; the + // nsSystemInfo object is typically created much later, when some piece + // of chrome JS wants it. The system call is specified as unable to fail. + nsSystemInfo::gUserUmask = ::umask(0777); + ::umask(nsSystemInfo::gUserUmask); +#endif + + // Set up chromium libs + NS_ASSERTION(!sExitManager && !sMessageLoop, "Bad logic!"); + + if (!AtExitManager::AlreadyRegistered()) { + sExitManager = new AtExitManager(); + } + + MessageLoop* messageLoop = MessageLoop::current(); + if (!messageLoop) { + sMessageLoop = new MessageLoopForUI(MessageLoop::TYPE_MOZILLA_PARENT); + sMessageLoop->set_thread_name("Gecko"); + // Set experimental values for main thread hangs: + // 128ms for transient hangs and 8192ms for permanent hangs + sMessageLoop->set_hang_timeouts(128, 8192); + } else if (messageLoop->type() == MessageLoop::TYPE_MOZILLA_CHILD) { + messageLoop->set_thread_name("Gecko_Child"); + messageLoop->set_hang_timeouts(128, 8192); + } + + if (XRE_IsParentProcess() && + !BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO)) { + UniquePtr<BrowserProcessSubThread> ioThread = MakeUnique<BrowserProcessSubThread>(BrowserProcessSubThread::IO); + + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + if (NS_WARN_IF(!ioThread->StartWithOptions(options))) { + return NS_ERROR_FAILURE; + } + + sIOThread = ioThread.release(); + } + + // Establish the main thread here. + rv = nsThreadManager::get().Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Set up the timer globals/timer thread + rv = nsTimerImpl::Startup(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#ifndef ANDROID + // If the locale hasn't already been setup by our embedder, + // get us out of the "C" locale and into the system + if (strcmp(setlocale(LC_ALL, nullptr), "C") == 0) { + setlocale(LC_ALL, ""); + } +#endif + +#if defined(XP_UNIX) + NS_StartupNativeCharsetUtils(); +#endif + + NS_StartupLocalFile(); + + StartupSpecialSystemDirectory(); + + nsDirectoryService::RealInit(); + + bool value; + + if (aBinDirectory) { + rv = aBinDirectory->IsDirectory(&value); + + if (NS_SUCCEEDED(rv) && value) { + nsDirectoryService::gService->Set(NS_XPCOM_INIT_CURRENT_PROCESS_DIR, + aBinDirectory); + } + } + + if (aAppFileLocationProvider) { + rv = nsDirectoryService::gService->RegisterProvider(aAppFileLocationProvider); + if (NS_FAILED(rv)) { + return rv; + } + } + + nsCOMPtr<nsIFile> xpcomLib; + nsDirectoryService::gService->Get(NS_GRE_BIN_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(xpcomLib)); + MOZ_ASSERT(xpcomLib); + + // set gGREBinPath + nsAutoString path; + xpcomLib->GetPath(path); + gGREBinPath = ToNewUnicode(path); + + xpcomLib->AppendNative(nsDependentCString(XPCOM_DLL)); + nsDirectoryService::gService->Set(NS_XPCOM_LIBRARY_FILE, xpcomLib); + + if (!mozilla::Omnijar::IsInitialized()) { + mozilla::Omnijar::Init(); + } + + if ((sCommandLineWasInitialized = !CommandLine::IsInitialized())) { +#ifdef OS_WIN + CommandLine::Init(0, nullptr); +#else + nsCOMPtr<nsIFile> binaryFile; + nsDirectoryService::gService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(binaryFile)); + if (NS_WARN_IF(!binaryFile)) { + return NS_ERROR_FAILURE; + } + + rv = binaryFile->AppendNative(NS_LITERAL_CSTRING("nonexistent-executable")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCString binaryPath; + rv = binaryFile->GetNativePath(binaryPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + static char const* const argv = { strdup(binaryPath.get()) }; + CommandLine::Init(1, &argv); +#endif + } + + NS_ASSERTION(nsComponentManagerImpl::gComponentManager == nullptr, + "CompMgr not null at init"); + + // Create the Component/Service Manager + nsComponentManagerImpl::gComponentManager = new nsComponentManagerImpl(); + NS_ADDREF(nsComponentManagerImpl::gComponentManager); + + // Global cycle collector initialization. + if (!nsCycleCollector_init()) { + return NS_ERROR_UNEXPECTED; + } + + // And start it up for this thread too. + nsCycleCollector_startup(); + + // Register ICU memory functions. This really shouldn't be necessary: the + // JS engine should do this on its own inside JS_Init, and memory-reporting + // code should call a JSAPI function to observe ICU memory usage. But we + // can't define the alloc/free functions in the JS engine, because it can't + // depend on the XPCOM-based memory reporting goop. So for now, we have + // this oddness. + mozilla::SetICUMemoryFunctions(); + + // Do the same for libogg. + ogg_set_mem_functions(OggReporter::CountingMalloc, + OggReporter::CountingCalloc, + OggReporter::CountingRealloc, + OggReporter::CountingFree); + +#if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING) + // And for VPX. + vpx_mem_set_functions(VPXReporter::CountingMalloc, + VPXReporter::CountingCalloc, + VPXReporter::CountingRealloc, + VPXReporter::CountingFree, + memcpy, + memset, + memmove); +#endif + +#if EXPOSE_INTL_API && defined(MOZ_ICU_DATA_ARCHIVE) + nsCOMPtr<nsIFile> greDir; + nsDirectoryService::gService->Get(NS_GRE_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(greDir)); + MOZ_ASSERT(greDir); + nsAutoCString nativeGREPath; + greDir->GetNativePath(nativeGREPath); + u_setDataDirectory(nativeGREPath.get()); +#endif + + // Initialize the JS engine. + const char* jsInitFailureReason = JS_InitWithFailureDiagnostic(); + if (jsInitFailureReason) { + NS_RUNTIMEABORT(jsInitFailureReason); + } + sInitializedJS = true; + + // Init AbstractThread. + AbstractThread::InitStatics(); + + rv = nsComponentManagerImpl::gComponentManager->Init(); + if (NS_FAILED(rv)) { + NS_RELEASE(nsComponentManagerImpl::gComponentManager); + return rv; + } + + if (aResult) { + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + } + + // The iimanager constructor searches and registers XPT files. + // (We trigger the singleton's lazy construction here to make that happen.) + (void)XPTInterfaceInfoManager::GetSingleton(); + + // After autoreg, but before we actually instantiate any components, + // add any services listed in the "xpcom-directory-providers" category + // to the directory service. + nsDirectoryService::gService->RegisterCategoryProviders(); + + // Init SharedThreadPool (which needs the service manager). + SharedThreadPool::InitStatics(); + + // Force layout to spin up so that nsContentUtils is available for cx stack + // munging. Note that layout registers a number of static atoms, and also + // seals the static atom table, so NS_RegisterStaticAtom may not be called + // beyond this point. + nsCOMPtr<nsISupports> componentLoader = + do_GetService("@mozilla.org/moz/jsloader;1"); + + mozilla::scache::StartupCache::GetSingleton(); + mozilla::AvailableMemoryTracker::Activate(); + + // Notify observers of xpcom autoregistration start + NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY, + nullptr, + NS_XPCOM_STARTUP_OBSERVER_ID); +#ifdef XP_WIN + CreateAnonTempFileRemover(); +#endif + + // We only want the SystemMemoryReporter running in one process, because it + // profiles the entire system. The main process is the obvious place for + // it. + if (XRE_IsParentProcess()) { + mozilla::SystemMemoryReporter::Init(); + } + + // The memory reporter manager is up and running -- register our reporters. + RegisterStrongMemoryReporter(new ICUReporter()); + RegisterStrongMemoryReporter(new OggReporter()); +#ifdef MOZ_VPX + RegisterStrongMemoryReporter(new VPXReporter()); +#endif + + mozilla::Telemetry::Init(); + + mozilla::HangMonitor::Startup(); + mozilla::BackgroundHangMonitor::Startup(); + + const MessageLoop* const loop = MessageLoop::current(); + sMainHangMonitor = new mozilla::BackgroundHangMonitor( + loop->thread_name().c_str(), + loop->transient_hang_timeout(), + loop->permanent_hang_timeout()); + + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +NS_InitMinimalXPCOM() +{ + mozPoisonValueInit(); + NS_SetMainThread(); + mozilla::TimeStamp::Startup(); + NS_LogInit(); + NS_InitAtomTable(); + mozilla::LogModule::Init(); + + char aLocal; + profiler_init(&aLocal); + + nsresult rv = nsThreadManager::get().Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Set up the timer globals/timer thread. + rv = nsTimerImpl::Startup(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Create the Component/Service Manager + nsComponentManagerImpl::gComponentManager = new nsComponentManagerImpl(); + NS_ADDREF(nsComponentManagerImpl::gComponentManager); + + rv = nsComponentManagerImpl::gComponentManager->Init(); + if (NS_FAILED(rv)) { + NS_RELEASE(nsComponentManagerImpl::gComponentManager); + return rv; + } + + // Global cycle collector initialization. + if (!nsCycleCollector_init()) { + return NS_ERROR_UNEXPECTED; + } + + AbstractThread::InitStatics(); + SharedThreadPool::InitStatics(); + mozilla::Telemetry::Init(); + mozilla::HangMonitor::Startup(); + mozilla::BackgroundHangMonitor::Startup(); + + return NS_OK; +} + +// +// NS_ShutdownXPCOM() +// +// The shutdown sequence for xpcom would be +// +// - Notify "xpcom-shutdown" for modules to release primary (root) references +// - Shutdown XPCOM timers +// - Notify "xpcom-shutdown-threads" for thread joins +// - Shutdown the event queues +// - Release the Global Service Manager +// - Release all service instances held by the global service manager +// - Release the Global Service Manager itself +// - Release the Component Manager +// - Release all factories cached by the Component Manager +// - Notify module loaders to shut down +// - Unload Libraries +// - Release Contractid Cache held by Component Manager +// - Release dll abstraction held by Component Manager +// - Release the Registry held by Component Manager +// - Finally, release the component manager itself +// +EXPORT_XPCOM_API(nsresult) +NS_ShutdownXPCOM(nsIServiceManager* aServMgr) +{ + return mozilla::ShutdownXPCOM(aServMgr); +} + +namespace mozilla { + +void +SetICUMemoryFunctions() +{ + static bool sICUReporterInitialized = false; + if (!sICUReporterInitialized) { + if (!JS_SetICUMemoryFunctions(ICUReporter::Alloc, ICUReporter::Realloc, + ICUReporter::Free)) { + NS_RUNTIMEABORT("JS_SetICUMemoryFunctions failed."); + } + sICUReporterInitialized = true; + } +} + +nsresult +ShutdownXPCOM(nsIServiceManager* aServMgr) +{ + // Make sure the hang monitor is enabled for shutdown. + HangMonitor::NotifyActivity(); + + if (!NS_IsMainThread()) { + NS_RUNTIMEABORT("Shutdown on wrong thread"); + } + + nsresult rv; + nsCOMPtr<nsISimpleEnumerator> moduleLoaders; + + // Notify observers of xpcom shutting down + { + // Block it so that the COMPtr will get deleted before we hit + // servicemanager shutdown + + nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); + if (NS_WARN_IF(!thread)) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<nsObserverService> observerService; + CallGetService("@mozilla.org/observer-service;1", + (nsObserverService**)getter_AddRefs(observerService)); + + if (observerService) { + mozilla::KillClearOnShutdown(ShutdownPhase::WillShutdown); + observerService->NotifyObservers(nullptr, + NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, + nullptr); + + nsCOMPtr<nsIServiceManager> mgr; + rv = NS_GetServiceManager(getter_AddRefs(mgr)); + if (NS_SUCCEEDED(rv)) { + mozilla::KillClearOnShutdown(ShutdownPhase::Shutdown); + observerService->NotifyObservers(mgr, NS_XPCOM_SHUTDOWN_OBSERVER_ID, + nullptr); + } + } + + // This must happen after the shutdown of media and widgets, which + // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification. + NS_ProcessPendingEvents(thread); + gfxPlatform::ShutdownLayersIPC(); + mozilla::dom::VideoDecoderManagerChild::Shutdown(); + + mozilla::scache::StartupCache::DeleteSingleton(); + if (observerService) + { + mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownThreads); + observerService->NotifyObservers(nullptr, + NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, + nullptr); + } + + gXPCOMThreadsShutDown = true; + NS_ProcessPendingEvents(thread); + + // Shutdown the timer thread and all timers that might still be alive before + // shutting down the component manager + nsTimerImpl::Shutdown(); + + NS_ProcessPendingEvents(thread); + + // Shutdown all remaining threads. This method does not return until + // all threads created using the thread manager (with the exception of + // the main thread) have exited. + nsThreadManager::get().Shutdown(); + + NS_ProcessPendingEvents(thread); + + HangMonitor::NotifyActivity(); + + // Late-write checks needs to find the profile directory, so it has to + // be initialized before mozilla::services::Shutdown or (because of + // xpcshell tests replacing the service) modules being unloaded. + mozilla::InitLateWriteChecks(); + + // We save the "xpcom-shutdown-loaders" observers to notify after + // the observerservice is gone. + if (observerService) { + mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownLoaders); + observerService->EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID, + getter_AddRefs(moduleLoaders)); + + observerService->Shutdown(); + } + } + + // Free ClearOnShutdown()'ed smart pointers. This needs to happen *after* + // we've finished notifying observers of XPCOM shutdown, because shutdown + // observers themselves might call ClearOnShutdown(). + mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal); + + // XPCOM is officially in shutdown mode NOW + // Set this only after the observers have been notified as this + // will cause servicemanager to become inaccessible. + mozilla::services::Shutdown(); + + // We may have AddRef'd for the caller of NS_InitXPCOM, so release it + // here again: + NS_IF_RELEASE(aServMgr); + + // Shutdown global servicemanager + if (nsComponentManagerImpl::gComponentManager) { + nsComponentManagerImpl::gComponentManager->FreeServices(); + } + + // Release the directory service + nsDirectoryService::gService = nullptr; + + free(gGREBinPath); + gGREBinPath = nullptr; + + if (moduleLoaders) { + bool more; + nsCOMPtr<nsISupports> el; + while (NS_SUCCEEDED(moduleLoaders->HasMoreElements(&more)) && more) { + moduleLoaders->GetNext(getter_AddRefs(el)); + + // Don't worry about weak-reference observers here: there is + // no reason for weak-ref observers to register for + // xpcom-shutdown-loaders + + // FIXME: This can cause harmless writes from sqlite committing + // log files. We have to ignore them before we can move + // the mozilla::PoisonWrite call before this point. See bug + // 834945 for the details. + nsCOMPtr<nsIObserver> obs(do_QueryInterface(el)); + if (obs) { + obs->Observe(nullptr, NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID, nullptr); + } + } + + moduleLoaders = nullptr; + } + + bool shutdownCollect; +#ifdef NS_FREE_PERMANENT_DATA + shutdownCollect = true; +#else + shutdownCollect = !!PR_GetEnv("MOZ_CC_RUN_DURING_SHUTDOWN"); +#endif + nsCycleCollector_shutdown(shutdownCollect); + + PROFILER_MARKER("Shutdown xpcom"); + // If we are doing any shutdown checks, poison writes. + if (gShutdownChecks != SCM_NOTHING) { +#ifdef XP_MACOSX + mozilla::OnlyReportDirtyWrites(); +#endif /* XP_MACOSX */ + mozilla::BeginLateWriteChecks(); + } + + // Shutdown nsLocalFile string conversion + NS_ShutdownLocalFile(); +#ifdef XP_UNIX + NS_ShutdownNativeCharsetUtils(); +#endif + + // Shutdown xpcom. This will release all loaders and cause others holding + // a refcount to the component manager to release it. + if (nsComponentManagerImpl::gComponentManager) { + rv = (nsComponentManagerImpl::gComponentManager)->Shutdown(); + NS_ASSERTION(NS_SUCCEEDED(rv), "Component Manager shutdown failed."); + } else { + NS_WARNING("Component Manager was never created ..."); + } + +#ifdef MOZ_ENABLE_PROFILER_SPS + // In optimized builds we don't do shutdown collections by default, so + // uncollected (garbage) objects may keep the nsXPConnect singleton alive, + // and its XPCJSContext along with it. However, we still destroy various + // bits of state in JS_ShutDown(), so we need to make sure the profiler + // can't access them when it shuts down. This call nulls out the + // JS pseudo-stack's internal reference to the main thread JSContext, + // duplicating the call in XPCJSContext::~XPCJSContext() in case that + // never fired. + if (PseudoStack* stack = mozilla_get_pseudo_stack()) { + stack->sampleContext(nullptr); + } +#endif + + if (sInitializedJS) { + // Shut down the JS engine. + JS_ShutDown(); + sInitializedJS = false; + } + + // Release our own singletons + // Do this _after_ shutting down the component manager, because the + // JS component loader will use XPConnect to call nsIModule::canUnload, + // and that will spin up the InterfaceInfoManager again -- bad mojo + XPTInterfaceInfoManager::FreeInterfaceInfoManager(); + + // Finally, release the component manager last because it unloads the + // libraries: + if (nsComponentManagerImpl::gComponentManager) { + nsrefcnt cnt; + NS_RELEASE2(nsComponentManagerImpl::gComponentManager, cnt); + NS_ASSERTION(cnt == 0, "Component Manager being held past XPCOM shutdown."); + } + nsComponentManagerImpl::gComponentManager = nullptr; + nsCategoryManager::Destroy(); + + NS_ShutdownAtomTable(); + + NS_IF_RELEASE(gDebug); + + delete sIOThread; + sIOThread = nullptr; + + delete sMessageLoop; + sMessageLoop = nullptr; + + if (sCommandLineWasInitialized) { + CommandLine::Terminate(); + sCommandLineWasInitialized = false; + } + + delete sExitManager; + sExitManager = nullptr; + + Omnijar::CleanUp(); + + HangMonitor::Shutdown(); + + delete sMainHangMonitor; + sMainHangMonitor = nullptr; + + BackgroundHangMonitor::Shutdown(); + + profiler_shutdown(); + + NS_LogTerm(); + +#if defined(MOZ_WIDGET_GONK) + // This _exit(0) call is intended to be temporary, to get shutdown leak + // checking working on non-B2G platforms. + // On debug B2G, the child process crashes very late. Instead, just + // give up so at least we exit cleanly. See bug 1071866. + if (XRE_IsContentProcess()) { + NS_WARNING("Exiting child process early!"); + _exit(0); + } +#endif + + return NS_OK; +} + +} // namespace mozilla diff --git a/xpcom/build/XPCOMModule.inc b/xpcom/build/XPCOMModule.inc new file mode 100644 index 000000000..5ac2885e2 --- /dev/null +++ b/xpcom/build/XPCOMModule.inc @@ -0,0 +1,81 @@ + COMPONENT_M(MEMORY, nsMemoryImpl::Create, Module::ALLOW_IN_GPU_PROCESS) + COMPONENT_M(DEBUG, nsDebugImpl::Create, Module::ALLOW_IN_GPU_PROCESS) + COMPONENT(ERRORSERVICE, nsErrorService::Create) + + COMPONENT_M(CATEGORYMANAGER, nsCategoryManager::Create, Module::ALLOW_IN_GPU_PROCESS) + + COMPONENT(SCRIPTABLEINPUTSTREAM, nsScriptableInputStream::Create) + COMPONENT(BINARYINPUTSTREAM, nsBinaryInputStreamConstructor) + COMPONENT(BINARYOUTPUTSTREAM, nsBinaryOutputStreamConstructor) + COMPONENT(STORAGESTREAM, nsStorageStreamConstructor) + COMPONENT(VERSIONCOMPARATOR, nsVersionComparatorImplConstructor) + COMPONENT(SCRIPTABLEBASE64ENCODER, nsScriptableBase64EncoderConstructor) + COMPONENT(PIPE, nsPipeConstructor) + + COMPONENT(PROPERTIES, nsPropertiesConstructor) + + COMPONENT(PERSISTENTPROPERTIES, nsPersistentProperties::Create) + + COMPONENT(SUPPORTSARRAY, nsSupportsArray::Create) + COMPONENT(ARRAY, nsArrayBase::XPCOMConstructor) + COMPONENT(CONSOLESERVICE, nsConsoleServiceConstructor) + COMPONENT(ATOMSERVICE, nsAtomServiceConstructor) + COMPONENT_M(OBSERVERSERVICE, nsObserverService::Create, Module::ALLOW_IN_GPU_PROCESS) + + COMPONENT_M(TIMER, nsTimerConstructor, Module::ALLOW_IN_GPU_PROCESS) + +#define COMPONENT_SUPPORTS(TYPE, Type) \ + COMPONENT(SUPPORTS_##TYPE, nsSupports##Type##Constructor) + + COMPONENT_SUPPORTS(ID, ID) + COMPONENT_SUPPORTS(STRING, String) + COMPONENT_SUPPORTS(CSTRING, CString) + COMPONENT_SUPPORTS(PRBOOL, PRBool) + COMPONENT_SUPPORTS(PRUINT8, PRUint8) + COMPONENT_SUPPORTS(PRUINT16, PRUint16) + COMPONENT_SUPPORTS(PRUINT32, PRUint32) + COMPONENT_SUPPORTS(PRUINT64, PRUint64) + COMPONENT_SUPPORTS(PRTIME, PRTime) + COMPONENT_SUPPORTS(CHAR, Char) + COMPONENT_SUPPORTS(PRINT16, PRInt16) + COMPONENT_SUPPORTS(PRINT32, PRInt32) + COMPONENT_SUPPORTS(PRINT64, PRInt64) + COMPONENT_SUPPORTS(FLOAT, Float) + COMPONENT_SUPPORTS(DOUBLE, Double) + COMPONENT_SUPPORTS(VOID, Void) + COMPONENT_SUPPORTS(INTERFACE_POINTER, InterfacePointer) + +#undef COMPONENT_SUPPORTS + COMPONENT(LOCAL_FILE, nsLocalFile::nsLocalFileConstructor) + COMPONENT(DIRECTORY_SERVICE, nsDirectoryService::Create) + COMPONENT(PROCESS, nsProcessConstructor) + COMPONENT(ENVIRONMENT, nsEnvironment::Create) + + COMPONENT(THREADMANAGER, nsThreadManagerGetSingleton) + COMPONENT_M(THREADPOOL, nsThreadPoolConstructor, Module::ALLOW_IN_GPU_PROCESS) + + COMPONENT(STRINGINPUTSTREAM, nsStringInputStreamConstructor) + COMPONENT(MULTIPLEXINPUTSTREAM, nsMultiplexInputStreamConstructor) + + COMPONENT(VARIANT, nsVariantCCConstructor) + COMPONENT(INTERFACEINFOMANAGER_SERVICE, nsXPTIInterfaceInfoManagerGetSingleton) + + COMPONENT(HASH_PROPERTY_BAG, nsHashPropertyBagCCConstructor) + + COMPONENT(UUID_GENERATOR, nsUUIDGeneratorConstructor) + +#if defined(XP_WIN) + COMPONENT(WINDOWSREGKEY, nsWindowsRegKeyConstructor) +#endif + +#if defined(MOZ_WIDGET_COCOA) + COMPONENT(MACUTILSIMPL, nsMacUtilsImplConstructor) +#endif + + COMPONENT(SYSTEMINFO, nsSystemInfoConstructor) + COMPONENT(MEMORY_REPORTER_MANAGER, nsMemoryReporterManagerConstructor) + COMPONENT(MEMORY_INFO_DUMPER, nsMemoryInfoDumperConstructor) + COMPONENT(IOUTIL, nsIOUtilConstructor) + COMPONENT(CYCLE_COLLECTOR_LOGGER, nsCycleCollectorLoggerConstructor) + COMPONENT(MESSAGE_LOOP, nsMessageLoopConstructor) + COMPONENT(STATUS_REPORTER_MANAGER, nsStatusReporterManagerConstructor) diff --git a/xpcom/build/XREChildData.h b/xpcom/build/XREChildData.h new file mode 100644 index 000000000..487fede94 --- /dev/null +++ b/xpcom/build/XREChildData.h @@ -0,0 +1,51 @@ +/* -*- 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/. */ + +#ifndef XREChildData_h +#define XREChildData_h + +#include "mozilla/UniquePtr.h" + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +#include "mozilla/sandboxing/loggingTypes.h" + +namespace sandbox { +class TargetServices; +} +#endif + +namespace mozilla { +namespace gmp { +class GMPLoader; +} +} + +/** + * Data needed to start a child process. + */ +struct XREChildData +{ +#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK) + /** + * Used to load the GMP binary. + */ + mozilla::UniquePtr<mozilla::gmp::GMPLoader> gmpLoader; +#endif + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + /** + * Chromium sandbox TargetServices. + */ + sandbox::TargetServices* sandboxTargetServices = nullptr; + + /** + * Function to provide a logging function to the chromium sandbox code. + */ + mozilla::sandboxing::ProvideLogFunctionCb ProvideLogFunction = nullptr; +#endif +}; + +#endif // XREChildData_h diff --git a/xpcom/build/XREShellData.h b/xpcom/build/XREShellData.h new file mode 100644 index 000000000..11bc162d9 --- /dev/null +++ b/xpcom/build/XREShellData.h @@ -0,0 +1,29 @@ +/* -*- 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/. */ + +#ifndef XREShellData_h +#define XREShellData_h + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +namespace sandbox { +class BrokerServices; +} +#endif + +/** + * Data needed by XRE_XPCShellMain. + */ +struct XREShellData +{ +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + /** + * Chromium sandbox BrokerServices. + */ + sandbox::BrokerServices* sandboxBrokerServices; +#endif +}; + +#endif // XREShellData_h diff --git a/xpcom/build/mach_override.c b/xpcom/build/mach_override.c new file mode 100644 index 000000000..9e4940d29 --- /dev/null +++ b/xpcom/build/mach_override.c @@ -0,0 +1,789 @@ +// Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443. +// mach_override.c semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#include "mach_override.h" + +#include <mach-o/dyld.h> +#include <mach/mach_host.h> +#include <mach/mach_init.h> +#include <mach/vm_map.h> +#include <sys/mman.h> + +#include <CoreServices/CoreServices.h> + +/************************** +* +* Constants +* +**************************/ +#pragma mark - +#pragma mark (Constants) + +#define kPageSize 4096 +#if defined(__ppc__) || defined(__POWERPC__) + +long kIslandTemplate[] = { + 0x9001FFFC, // stw r0,-4(SP) + 0x3C00DEAD, // lis r0,0xDEAD + 0x6000BEEF, // ori r0,r0,0xBEEF + 0x7C0903A6, // mtctr r0 + 0x8001FFFC, // lwz r0,-4(SP) + 0x60000000, // nop ; optionally replaced + 0x4E800420 // bctr +}; + +#define kAddressHi 3 +#define kAddressLo 5 +#define kInstructionHi 10 +#define kInstructionLo 11 + +#elif defined(__i386__) + +#define kOriginalInstructionsSize 16 +// On X86 we migh need to instert an add with a 32 bit immediate after the +// original instructions. +#define kMaxFixupSizeIncrease 5 + +unsigned char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xE9, 0xEF, 0xBE, 0xAD, 0xDE +}; + +#define kInstructions 0 +#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 +#elif defined(__x86_64__) + +#define kOriginalInstructionsSize 32 +// On X86-64 we never need to instert a new instruction. +#define kMaxFixupSizeIncrease 0 + +#define kJumpAddress kOriginalInstructionsSize + 6 + +unsigned char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#endif + +/************************** +* +* Data Types +* +**************************/ +#pragma mark - +#pragma mark (Data Types) + +typedef struct { + char instructions[sizeof(kIslandTemplate)]; +} BranchIsland; + +/************************** +* +* Funky Protos +* +**************************/ +#pragma mark - +#pragma mark (Funky Protos) + +static mach_error_t +allocateBranchIsland( + BranchIsland **island, + void *originalFunctionAddress); + + mach_error_t +freeBranchIsland( + BranchIsland *island ); + +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ); +#endif + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ); +void +atomic_mov64( + uint64_t *targetAddress, + uint64_t value ); + + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ); + + static void +fixupInstructions( + uint32_t offset, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ); +#endif + +/******************************************************************************* +* +* Interface +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Interface) + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; + uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); + int e = err_none; + e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); + e |= msync((void *)page, kPageSize, MS_INVALIDATE ); + if (e) { + err = err_cannot_override; + } + return err; +} +#endif + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ) +{ + assert( originalFunctionAddress ); + assert( overrideFunctionAddress ); + + // this addresses overriding such functions as AudioOutputUnitStart() + // test with modified DefaultOutputUnit project +#if defined(__x86_64__) + for(;;){ + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] + originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); + else break; + } +#elif defined(__i386__) + for(;;){ + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? + originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); + else break; + } +#endif + + long *originalFunctionPtr = (long*) originalFunctionAddress; + mach_error_t err = err_none; + +#if defined(__ppc__) || defined(__POWERPC__) + // Ensure first instruction isn't 'mfctr'. + #define kMFCTRMask 0xfc1fffff + #define kMFCTRInstruction 0x7c0903a6 + + long originalInstruction = *originalFunctionPtr; + if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) + err = err_cannot_override; +#elif defined(__i386__) || defined(__x86_64__) + int eatenCount = 0; + int originalInstructionCount = 0; + char originalInstructions[kOriginalInstructionsSize]; + uint8_t originalInstructionSizes[kOriginalInstructionsSize]; + uint64_t jumpRelativeInstruction = 0; // JMP + + Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, + &jumpRelativeInstruction, &eatenCount, + originalInstructions, &originalInstructionCount, + originalInstructionSizes ); + if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { + //printf ("Too many instructions eaten\n"); + overridePossible = false; + } + if (!overridePossible) err = err_cannot_override; + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); +#endif + + // Make the original function implementation writable. + if( !err ) { + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_ALL | VM_PROT_COPY) ); + if( err ) + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_DEFAULT | VM_PROT_COPY) ); + } + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + // Allocate and target the escape island to the overriding function. + BranchIsland *escapeIsland = NULL; + if( !err ) + err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + +#if defined(__ppc__) || defined(__POWERPC__) + if( !err ) + err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); + + // Build the branch absolute instruction to the escape island. + long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. + if( !err ) { + long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; + branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; + } +#elif defined(__i386__) || defined(__x86_64__) + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + if( !err ) + err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); + + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + // Build the jump relative instruction to the escape island +#endif + + +#if defined(__i386__) || defined(__x86_64__) + if (!err) { + uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); + addressOffset = OSSwapInt32(addressOffset); + + jumpRelativeInstruction |= 0xE900000000000000LL; + jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; + jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); + } +#endif + + // Optionally allocate & return the reentry island. This may contain relocated + // jmp instructions and so has all the same addressing reachability requirements + // the escape island has to the original function, except the escape island is + // technically our original function. + BranchIsland *reentryIsland = NULL; + if( !err && originalFunctionReentryIsland ) { + err = allocateBranchIsland( &reentryIsland, escapeIsland); + if( !err ) + *originalFunctionReentryIsland = reentryIsland; + } + +#if defined(__ppc__) || defined(__POWERPC__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instruction into the reentry island. + // o Target the reentry island at the 2nd instruction of the + // original function. + // o Replace the original instruction with the branch absolute. + if( !err ) { + int escapeIslandEngaged = false; + do { + if( reentryIsland ) + err = setBranchIslandTarget( reentryIsland, + (void*) (originalFunctionPtr+1), originalInstruction ); + if( !err ) { + escapeIslandEngaged = CompareAndSwap( originalInstruction, + branchAbsoluteInstruction, + (UInt32*)originalFunctionPtr ); + if( !escapeIslandEngaged ) { + // Someone replaced the instruction out from under us, + // re-read the instruction, make sure it's still not + // 'mfctr' and try again. + originalInstruction = *originalFunctionPtr; + if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) + err = err_cannot_override; + } + } + } while( !err && !escapeIslandEngaged ); + } +#elif defined(__i386__) || defined(__x86_64__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instructions into the reentry island. + // o Target the reentry island at the first non-replaced + // instruction of the original function. + // o Replace the original first instructions with the jump relative. + // + // Note that on i386, we do not support someone else changing the code under our feet + if ( !err ) { + uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; + fixupInstructions(offset, originalInstructions, + originalInstructionCount, originalInstructionSizes ); + + if( reentryIsland ) + err = setBranchIslandTarget_i386( reentryIsland, + (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); + // try making islands executable before planting the jmp +#if defined(__x86_64__) || defined(__i386__) + if( !err ) + err = makeIslandExecutable(escapeIsland); + if( !err && reentryIsland ) + err = makeIslandExecutable(reentryIsland); +#endif + if ( !err ) + atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); + } +#endif + + // Clean up on error. + if( err ) { + if( reentryIsland ) + freeBranchIsland( reentryIsland ); + if( escapeIsland ) + freeBranchIsland( escapeIsland ); + } + + return err; +} + +/******************************************************************************* +* +* Implementation +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Implementation) + +static bool jump_in_range(intptr_t from, intptr_t to) { + intptr_t field_value = to - from - 5; + int32_t field_value_32 = field_value; + return field_value == field_value_32; +} + +/******************************************************************************* + Implementation: Allocates memory for a branch island. + + @param island <- The allocated island. + @result <- mach_error_t + + ***************************************************************************/ + +static mach_error_t +allocateBranchIslandAux( + BranchIsland **island, + void *originalFunctionAddress, + bool forward) +{ + assert( island ); + assert( sizeof( BranchIsland ) <= kPageSize ); + + vm_map_t task_self = mach_task_self(); + vm_address_t original_address = (vm_address_t) originalFunctionAddress; + vm_address_t address = original_address; + + for (;;) { + vm_size_t vmsize = 0; + memory_object_name_t object = 0; + kern_return_t kr = 0; + vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; + // Find the region the address is in. +#if __WORDSIZE == 32 + vm_region_basic_info_data_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; + kr = vm_region(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); +#else + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + kr = vm_region_64(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); +#endif + if (kr != KERN_SUCCESS) + return kr; + assert((address & (kPageSize - 1)) == 0); + + // Go to the first page before or after this region + vm_address_t new_address = forward ? address + vmsize : address - kPageSize; +#if __WORDSIZE == 64 + if(!jump_in_range(original_address, new_address)) + break; +#endif + address = new_address; + + // Try to allocate this page. + kr = vm_allocate(task_self, &address, kPageSize, 0); + if (kr == KERN_SUCCESS) { + *island = (BranchIsland*) address; + return err_none; + } + if (kr != KERN_NO_SPACE) + return kr; + } + + return KERN_NO_SPACE; +} + +static mach_error_t +allocateBranchIsland( + BranchIsland **island, + void *originalFunctionAddress) +{ + mach_error_t err = + allocateBranchIslandAux(island, originalFunctionAddress, true); + if (!err) + return err; + return allocateBranchIslandAux(island, originalFunctionAddress, false); +} + + +/******************************************************************************* + Implementation: Deallocates memory for a branch island. + + @param island -> The island to deallocate. + @result <- mach_error_t + + ***************************************************************************/ + + mach_error_t +freeBranchIsland( + BranchIsland *island ) +{ + assert( island ); + assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); + assert( sizeof( BranchIsland ) <= kPageSize ); + return vm_deallocate( mach_task_self(), (vm_address_t) island, + kPageSize ); +} + +/******************************************************************************* + Implementation: Sets the branch island's target, with an optional + instruction. + + @param island -> The branch island to insert target into. + @param branchTo -> The address of the target. + @param instruction -> Optional instruction to execute prior to branch. Set + to zero for nop. + @result <- mach_error_t + + ***************************************************************************/ +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Fill in the address. + ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; + ((short*)island->instructions)[kAddressHi] + = (((long) branchTo) >> 16) & 0x0000FFFF; + + // Fill in the (optional) instuction. + if( instruction != 0 ) { + ((short*)island->instructions)[kInstructionLo] + = instruction & 0x0000FFFF; + ((short*)island->instructions)[kInstructionHi] + = (instruction >> 16) & 0x0000FFFF; + } + + //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + +#if defined(__i386__) + mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // copy original instructions + if (instructions) { + bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); + } + + // Fill in the address. + int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); + *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; + + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + return err_none; +} + +#elif defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Copy original instructions. + if (instructions) { + bcopy (instructions, island->instructions, kOriginalInstructionsSize); + } + + // Fill in the address. + *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + + +#if defined(__i386__) || defined(__x86_64__) +// simplistic instruction matching +typedef struct { + unsigned int length; // max 15 + unsigned char mask[15]; // sequence of bytes in memory order + unsigned char constraint[15]; // sequence of bytes in memory order +} AsmInstructionMatch; + +#if defined(__i386__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xFF}, {0x55} }, // push %esp + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp + { 0x1, {0xFF}, {0x53} }, // push %ebx + { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp + { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate + { 0x1, {0xFF}, {0x57} }, // push %edi + { 0x1, {0xFF}, {0x56} }, // push %esi + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg + { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax + { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax + { 0x0 } +}; +#elif defined(__x86_64__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xF8}, {0x50} }, // push %rX + { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp + { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx + { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX + { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX + { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg + { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax + + //leaq offset(%rip),%rax + { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} }, + + { 0x0 } +}; +#endif + +static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) +{ + Boolean match = true; + + size_t i; + for (i=0; i<instruction->length; i++) { + unsigned char mask = instruction->mask[i]; + unsigned char constraint = instruction->constraint[i]; + unsigned char codeValue = code[i]; + + match = ((codeValue & mask) == constraint); + if (!match) break; + } + + return match; +} + +#if defined(__i386__) || defined(__x86_64__) + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ) +{ + Boolean allInstructionsKnown = true; + int totalEaten = 0; + unsigned char* ptr = code; + int remainsToEat = 5; // a JMP instruction takes 5 bytes + int instructionIndex = 0; + + if (howManyEaten) *howManyEaten = 0; + if (originalInstructionCount) *originalInstructionCount = 0; + while (remainsToEat > 0) { + Boolean curInstructionKnown = false; + + // See if instruction matches one we know + AsmInstructionMatch* curInstr = possibleInstructions; + do { + if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; + curInstr++; + } while (curInstr->length > 0); + + // if all instruction matches failed, we don't know current instruction then, stop here + if (!curInstructionKnown) { + allInstructionsKnown = false; + fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); + break; + } + + // At this point, we've matched curInstr + int eaten = curInstr->length; + ptr += eaten; + remainsToEat -= eaten; + totalEaten += eaten; + + if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; + instructionIndex += 1; + if (originalInstructionCount) *originalInstructionCount = instructionIndex; + } + + + if (howManyEaten) *howManyEaten = totalEaten; + + if (originalInstructions) { + Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); + + if (enoughSpaceForOriginalInstructions) { + memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP + bcopy(code, originalInstructions, totalEaten); + } else { + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); + return false; + } + } + + if (allInstructionsKnown) { + // save last 3 bytes of first 64bits of codre we'll replace + uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); + currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation + currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; + + // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr + *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes + *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes + } + + return allInstructionsKnown; +} + + static void +fixupInstructions( + uint32_t offset, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ) +{ + // The start of "leaq offset(%rip),%rax" + static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05}; + + int index; + for (index = 0;index < instructionCount;index += 1) + { + if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative + { + uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); + *jumpOffsetPtr += offset; + } + + // leaq offset(%rip),%rax + if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) { + uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3); + *LeaqOffsetPtr += offset; + } + + // 32-bit call relative to the next addr; pop %eax + if (*(uint8_t*)instructionsToFix == 0xE8) + { + // Just this call is larger than the jump we use, so we + // know this is the last instruction. + assert(index == (instructionCount - 1)); + assert(instructionSizes[index] == 6); + + // Insert "addl $offset, %eax" in the end so that when + // we jump to the rest of the function %eax has the + // value it would have if eip had been pushed by the + // call in its original position. + uint8_t *op = instructionsToFix; + op += 6; + *op = 0x05; // addl + uint32_t *addImmPtr = (uint32_t*)(op + 1); + *addImmPtr = offset; + } + + instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); + } +} +#endif + +#if defined(__i386__) +__asm( + ".text;" + ".align 2, 0x90;" + "_atomic_mov64:;" + " pushl %ebp;" + " movl %esp, %ebp;" + " pushl %esi;" + " pushl %ebx;" + " pushl %ecx;" + " pushl %eax;" + " pushl %edx;" + + // atomic push of value to an address + // we use cmpxchg8b, which compares content of an address with + // edx:eax. If they are equal, it atomically puts 64bit value + // ecx:ebx in address. + // We thus put contents of address in edx:eax to force ecx:ebx + // in address + " mov 8(%ebp), %esi;" // esi contains target address + " mov 12(%ebp), %ebx;" + " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address + " mov (%esi), %eax;" + " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address + " lock; cmpxchg8b (%esi);" // atomic move. + + // restore registers + " popl %edx;" + " popl %eax;" + " popl %ecx;" + " popl %ebx;" + " popl %esi;" + " popl %ebp;" + " ret" +); +#elif defined(__x86_64__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value ) +{ + *targetAddress = value; +} +#endif +#endif diff --git a/xpcom/build/mach_override.h b/xpcom/build/mach_override.h new file mode 100644 index 000000000..d9be988a3 --- /dev/null +++ b/xpcom/build/mach_override.h @@ -0,0 +1,121 @@ +/******************************************************************************* + mach_override.h + Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> + Some rights reserved: <http://opensource.org/licenses/mit-license.php> + + ***************************************************************************/ + +/***************************************************************************//** + @mainpage mach_override + @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> + + This package, coded in C to the Mach API, allows you to override ("patch") + program- and system-supplied functions at runtime. You can fully replace + functions with your implementations, or merely head- or tail-patch the + original implementations. + + Use it by #include'ing mach_override.h from your .c, .m or .mm file(s). + + @todo Discontinue use of Carbon's MakeDataExecutable() and + CompareAndSwap() calls and start using the Mach equivalents, if they + exist. If they don't, write them and roll them in. That way, this + code will be pure Mach, which will make it easier to use everywhere. + Update: MakeDataExecutable() has been replaced by + msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but + I'm currently unsure if I can link against it. May have to roll in + my own version... + @todo Stop using an entire 4K high-allocated VM page per 28-byte escape + branch island. Done right, this will dramatically speed up escape + island allocations when they number over 250. Then again, if you're + overriding more than 250 functions, maybe speed isn't your main + concern... + @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl + first-instructions. Initially, we should refuse to override + functions beginning with these instructions. Eventually, we should + dynamically rewrite them to make them position-independent. + @todo Write mach_unoverride(), which would remove an override placed on a + function. Must be multiple-override aware, which means an almost + complete rewrite under the covers, because the target address can't + be spread across two load instructions like it is now since it will + need to be atomically updatable. + @todo Add non-rentry variants of overrides to test_mach_override. + + ***************************************************************************/ + +#ifndef _mach_override_ +#define _mach_override_ + +#include <sys/types.h> +#include <mach/error.h> + +#ifdef __cplusplus + extern "C" { +#endif + +/** + Returned if the function to be overrided begins with a 'mfctr' instruction. +*/ +#define err_cannot_override (err_local|1) + +/************************************************************************************//** + Dynamically overrides the function implementation referenced by + originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. + Optionally returns a pointer to a "reentry island" which, if jumped to, will resume + the original implementation. + + @param originalFunctionAddress -> Required address of the function to + override (with overrideFunctionAddress). + @param overrideFunctionAddress -> Required address to the overriding + function. + @param originalFunctionReentryIsland <- Optional pointer to pointer to the + reentry island. Can be nullptr. + @result <- err_cannot_override if the original + function's implementation begins with + the 'mfctr' instruction. + + ************************************************************************************/ + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ); + +/************************************************************************************//** + + + ************************************************************************************/ + +#ifdef __cplusplus + +#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ + { \ + static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ + static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ + class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ + public: \ + static kern_return_t override(void *originalFunctionPtr) { \ + kern_return_t result = err_none; \ + if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ + ORIGINAL_FUNCTION_NAME##_overriden = true; \ + result = mach_override_ptr( (void*)originalFunctionPtr, \ + (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ + (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ + } \ + return result; \ + } \ + static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { + +#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ + } \ + }; \ + \ + err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ + } + +#endif + +#ifdef __cplusplus + } +#endif +#endif // _mach_override_ diff --git a/xpcom/build/moz.build b/xpcom/build/moz.build new file mode 100644 index 000000000..68bd001a2 --- /dev/null +++ b/xpcom/build/moz.build @@ -0,0 +1,102 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS += [ + 'nsXPCOM.h', + 'nsXPCOMCID.h', + 'nsXPCOMCIDInternal.h', + 'nsXREAppData.h', + 'nsXULAppAPI.h', + 'XREChildData.h', + 'xrecore.h', + 'XREShellData.h', +] + +EXPORTS.mozilla += [ + 'FileLocation.h', + 'IOInterposer.h', + 'LateWriteChecks.h', + 'Omnijar.h', + 'PoisonIOInterposer.h', + 'ServiceList.h', + 'Services.h', + 'XPCOM.h', +] + +if CONFIG['OS_ARCH'] == 'WINNT': + EXPORTS += ['nsWindowsDllInterceptor.h'] + EXPORTS.mozilla += ['perfprobe.h'] + SOURCES += [ + 'perfprobe.cpp', + 'PoisonIOInterposerBase.cpp', + 'PoisonIOInterposerWin.cpp', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + UNIFIED_SOURCES += [ + 'PoisonIOInterposerBase.cpp', + 'PoisonIOInterposerMac.cpp', + ] + SOURCES += ['mach_override.c'] + SOURCES['mach_override.c'].flags += ['-Wno-unused-function'] +else: + SOURCES += ['PoisonIOInterposerStub.cpp'] + +include('../glue/objs.mozbuild') + +UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs +UNIFIED_SOURCES += xpcom_glue_src_cppsrcs + +UNIFIED_SOURCES += [ + 'FrozenFunctions.cpp', + 'IOInterposer.cpp', + 'LateWriteChecks.cpp', + 'MainThreadIOLogger.cpp', + 'nsXPCOMStrings.cpp', + 'Services.cpp', + 'XPCOMInit.cpp', +] + +if CONFIG['OS_ARCH'] != 'WINNT': + SOURCES += [ + 'NSPRInterposer.cpp', + ] + +# FileLocation.cpp and Omnijar.cpp cannot be built in unified mode because they +# use plarena.h. +SOURCES += [ + 'FileLocation.cpp', + 'Omnijar.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +DEFINES['_IMPL_NS_STRINGAPI'] = True +DEFINES['OMNIJAR_NAME'] = CONFIG['OMNIJAR_NAME'] +if CONFIG['MOZ_ICU_DATA_ARCHIVE']: + DEFINES['MOZ_ICU_DATA_ARCHIVE'] = True + +LOCAL_INCLUDES += [ + '!..', + '../base', + '../components', + '../ds', + '../glue', + '../io', + '../reflect/xptinfo', + '../threads', + '/chrome', + '/docshell/base', +] + +if CONFIG['MOZ_VPX']: + LOCAL_INCLUDES += [ + '/media/libvpx', + ] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/xpcom/build/nsWindowsDllInterceptor.h b/xpcom/build/nsWindowsDllInterceptor.h new file mode 100644 index 000000000..f7b7c93fb --- /dev/null +++ b/xpcom/build/nsWindowsDllInterceptor.h @@ -0,0 +1,1127 @@ +/* -*- 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/. */ + +#ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_ +#define NS_WINDOWS_DLL_INTERCEPTOR_H_ + +#include "mozilla/Assertions.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/UniquePtr.h" +#include "nsWindowsHelpers.h" + +#include <wchar.h> +#include <windows.h> +#include <winternl.h> + +/* + * Simple function interception. + * + * We have two separate mechanisms for intercepting a function: We can use the + * built-in nop space, if it exists, or we can create a detour. + * + * Using the built-in nop space works as follows: On x86-32, DLL functions + * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of + * NOP instructions. + * + * When we detect a function with this prelude, we do the following: + * + * 1. Write a long jump to our interceptor function into the five bytes of NOPs + * before the function. + * + * 2. Write a short jump -5 into the two-byte nop at the beginning of the function. + * + * This mechanism is nice because it's thread-safe. It's even safe to do if + * another thread is currently running the function we're modifying! + * + * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump + * but not the long jump, so re-intercepting the same function won't work, + * because its prelude won't match. + * + * + * Unfortunately nop space patching doesn't work on functions which don't have + * this magic prelude (and in particular, x86-64 never has the prelude). So + * when we can't use the built-in nop space, we fall back to using a detour, + * which works as follows: + * + * 1. Save first N bytes of OrigFunction to trampoline, where N is a + * number of bytes >= 5 that are instruction aligned. + * + * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook + * function. + * + * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to + * continue original program flow. + * + * 4. Hook function needs to call the trampoline during its execution, + * to invoke the original function (so address of trampoline is + * returned). + * + * When the WindowsDllDetourPatcher object is destructed, OrigFunction is + * patched again to jump directly to the trampoline instead of going through + * the hook function. As such, re-intercepting the same function won't work, as + * jump instructions are not supported. + * + * Note that this is not thread-safe. Sad day. + * + */ + +#include <stdint.h> + +namespace mozilla { +namespace internal { + +class AutoVirtualProtect +{ +public: + AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect) + : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0), + mSuccess(false) + {} + + ~AutoVirtualProtect() + { + if (mSuccess) { + VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect, + &mOldProtect); + } + } + + bool Protect() + { + mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, + mNewProtect, &mOldProtect); + if (!mSuccess) { + // printf("VirtualProtectEx failed! %d\n", GetLastError()); + } + return mSuccess; + } + +private: + void* const mFunc; + size_t const mSize; + DWORD const mNewProtect; + DWORD mOldProtect; + bool mSuccess; +}; + +class WindowsDllNopSpacePatcher +{ + typedef uint8_t* byteptr_t; + HMODULE mModule; + + // Dumb array for remembering the addresses of functions we've patched. + // (This should be nsTArray, but non-XPCOM code uses this class.) + static const size_t maxPatchedFns = 128; + byteptr_t mPatchedFns[maxPatchedFns]; + int mPatchedFnsLen; + +public: + WindowsDllNopSpacePatcher() + : mModule(0) + , mPatchedFnsLen(0) + {} + +#if defined(_M_IX86) + ~WindowsDllNopSpacePatcher() + { + // Restore the mov edi, edi to the beginning of each function we patched. + + for (int i = 0; i < mPatchedFnsLen; i++) { + byteptr_t fn = mPatchedFns[i]; + + // Ensure we can write to the code. + AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE); + if (!protect.Protect()) { + continue; + } + + // mov edi, edi + *((uint16_t*)fn) = 0xff8b; + + // I don't think this is actually necessary, but it can't hurt. + FlushInstructionCache(GetCurrentProcess(), + /* ignored */ nullptr, + /* ignored */ 0); + } + } + + void Init(const char* aModuleName) + { + if (!IsCompatible()) { +#if defined(MOZILLA_INTERNAL_API) + NS_WARNING("NOP space patching is unavailable for compatibility reasons"); +#endif + return; + } + + mModule = LoadLibraryExA(aModuleName, nullptr, 0); + if (!mModule) { + //printf("LoadLibraryEx for '%s' failed\n", aModuleName); + return; + } + } + + /** + * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions + * in our address space. There is a bug in Detours 2.x that causes it to + * patch at the wrong address when attempting to detour code that is already + * NOP space patched. This function is an effort to detect the presence of + * this NVIDIA code in our address space and disable NOP space patching if it + * is. We also check AppInit_DLLs since this is the mechanism that the Optimus + * drivers use to inject into our process. + */ + static bool IsCompatible() + { + // These DLLs are known to have bad interactions with this style of patching + const wchar_t* kIncompatibleDLLs[] = { + L"detoured.dll", + L"_etoured.dll", + L"nvd3d9wrap.dll", + L"nvdxgiwrap.dll" + }; + // See if the infringing DLLs are already loaded + for (unsigned int i = 0; i < mozilla::ArrayLength(kIncompatibleDLLs); ++i) { + if (GetModuleHandleW(kIncompatibleDLLs[i])) { + return false; + } + } + if (GetModuleHandleW(L"user32.dll")) { + // user32 is loaded but the infringing DLLs are not, assume we're safe to + // proceed. + return true; + } + // If user32 has not loaded yet, check AppInit_DLLs to ensure that Optimus + // won't be loaded once user32 is initialized. + HKEY hkey = NULL; + if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", + 0, KEY_QUERY_VALUE, &hkey)) { + nsAutoRegKey key(hkey); + DWORD numBytes = 0; + const wchar_t kAppInitDLLs[] = L"AppInit_DLLs"; + // Query for required buffer size + LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, + nullptr, nullptr, &numBytes); + mozilla::UniquePtr<wchar_t[]> data; + if (!status) { + // Allocate the buffer and query for the actual data + data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t)); + status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, + nullptr, (LPBYTE)data.get(), &numBytes); + } + if (!status) { + // For each token, split up the filename components and then check the + // name of the file. + const wchar_t kDelimiters[] = L", "; + wchar_t* tokenContext = nullptr; + wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext); + while (token) { + wchar_t fname[_MAX_FNAME] = {0}; + if (!_wsplitpath_s(token, nullptr, 0, nullptr, 0, + fname, mozilla::ArrayLength(fname), + nullptr, 0)) { + // nvinit.dll is responsible for bootstrapping the DLL injection, so + // that is the library that we check for here + const wchar_t kNvInitName[] = L"nvinit"; + if (!_wcsnicmp(fname, kNvInitName, + mozilla::ArrayLength(kNvInitName))) { + return false; + } + } + token = wcstok_s(nullptr, kDelimiters, &tokenContext); + } + } + } + return true; + } + + bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc) + { + if (!mModule) { + return false; + } + + if (!IsCompatible()) { +#if defined(MOZILLA_INTERNAL_API) + NS_WARNING("NOP space patching is unavailable for compatibility reasons"); +#endif + return false; + } + + if (mPatchedFnsLen == maxPatchedFns) { + // printf ("No space for hook in mPatchedFns.\n"); + return false; + } + + byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName)); + if (!fn) { + //printf ("GetProcAddress failed\n"); + return false; + } + + fn = ResolveRedirectedAddress(fn); + + // Ensure we can read and write starting at fn - 5 (for the long jmp we're + // going to write) and ending at fn + 2 (for the short jmp up to the long + // jmp). These bytes may span two pages with different protection. + AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE); + AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE); + if (!protectBefore.Protect() || !protectAfter.Protect()) { + return false; + } + + bool rv = WriteHook(fn, aHookDest, aOrigFunc); + + if (rv) { + mPatchedFns[mPatchedFnsLen] = fn; + mPatchedFnsLen++; + } + + return rv; + } + + bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc) + { + // Check that the 5 bytes before aFn are NOP's or INT 3's, + // and that the 2 bytes after aFn are mov(edi, edi). + // + // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE + // before calling WriteHook. + + for (int i = -5; i <= -1; i++) { + if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3 + return false; + } + } + + // mov edi, edi. Yes, there are two ways to encode the same thing: + // + // 0x89ff == mov r/m, r + // 0x8bff == mov r, r/m + // + // where "r" is register and "r/m" is register or memory. Windows seems to + // use 8bff; I include 89ff out of paranoia. + if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) { + return false; + } + + // Write a long jump into the space above the function. + aFn[-5] = 0xe9; // jmp + *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement + + // Set aOrigFunc here, because after this point, aHookDest might be called, + // and aHookDest might use the aOrigFunc pointer. + *aOrigFunc = aFn + 2; + + // Short jump up into our long jump. + *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5 + + // I think this routine is safe without this, but it can't hurt. + FlushInstructionCache(GetCurrentProcess(), + /* ignored */ nullptr, + /* ignored */ 0); + + return true; + } + +private: + static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction) + { + // If function entry is jmp [disp32] such as used by kernel32, + // we resolve redirected address from import table. + if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) { + return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2))); + } + + return aOriginalFunction; + } +#else + void Init(const char* aModuleName) + { + // Not implemented except on x86-32. + } + + bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc) + { + // Not implemented except on x86-32. + return false; + } +#endif +}; + +class WindowsDllDetourPatcher +{ + typedef unsigned char* byteptr_t; +public: + WindowsDllDetourPatcher() + : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0) + { + } + + ~WindowsDllDetourPatcher() + { + int i; + byteptr_t p; + for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) { +#if defined(_M_IX86) + size_t nBytes = 1 + sizeof(intptr_t); +#elif defined(_M_X64) + size_t nBytes = 2 + sizeof(intptr_t); +#else +#error "Unknown processor type" +#endif + byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p)); + + // ensure we can modify the original code + AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE); + if (!protect.Protect()) { + continue; + } + + // Remove the hook by making the original function jump directly + // in the trampoline. + intptr_t dest = (intptr_t)(p + sizeof(void*)); +#if defined(_M_IX86) + // Ensure the JMP from CreateTrampoline is where we expect it to be. + if (origBytes[0] != 0xE9) + continue; + *((intptr_t*)(origBytes + 1)) = + dest - (intptr_t)(origBytes + 5); // target displacement +#elif defined(_M_X64) + // Ensure the MOV R11 from CreateTrampoline is where we expect it to be. + if (origBytes[0] != 0x49 || origBytes[1] != 0xBB) + continue; + *((intptr_t*)(origBytes + 2)) = dest; +#else +#error "Unknown processor type" +#endif + } + } + + void Init(const char* aModuleName, int aNumHooks = 0) + { + if (mModule) { + return; + } + + mModule = LoadLibraryExA(aModuleName, nullptr, 0); + if (!mModule) { + //printf("LoadLibraryEx for '%s' failed\n", aModuleName); + return; + } + + int hooksPerPage = 4096 / kHookSize; + if (aNumHooks == 0) { + aNumHooks = hooksPerPage; + } + + mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks); + + mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr, + mMaxHooks * kHookSize, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READ); + if (!mHookPage) { + mModule = 0; + return; + } + } + + bool Initialized() { return !!mModule; } + + bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc) + { + if (!mModule) { + return false; + } + + void* pAddr = (void*)GetProcAddress(mModule, aName); + if (!pAddr) { + //printf ("GetProcAddress failed\n"); + return false; + } + + pAddr = ResolveRedirectedAddress((byteptr_t)pAddr); + + CreateTrampoline(pAddr, aHookDest, aOrigFunc); + if (!*aOrigFunc) { + //printf ("CreateTrampoline failed\n"); + return false; + } + + return true; + } + +protected: + const static int kPageSize = 4096; + const static int kHookSize = 128; + + HMODULE mModule; + byteptr_t mHookPage; + int mMaxHooks; + int mCurHooks; + + // rex bits + static const BYTE kMaskHighNibble = 0xF0; + static const BYTE kRexOpcode = 0x40; + static const BYTE kMaskRexW = 0x08; + static const BYTE kMaskRexR = 0x04; + static const BYTE kMaskRexX = 0x02; + static const BYTE kMaskRexB = 0x01; + + // mod r/m bits + static const BYTE kRegFieldShift = 3; + static const BYTE kMaskMod = 0xC0; + static const BYTE kMaskReg = 0x38; + static const BYTE kMaskRm = 0x07; + static const BYTE kRmNeedSib = 0x04; + static const BYTE kModReg = 0xC0; + static const BYTE kModDisp32 = 0x80; + static const BYTE kModDisp8 = 0x40; + static const BYTE kModNoRegDisp = 0x00; + static const BYTE kRmNoRegDispDisp32 = 0x05; + + // sib bits + static const BYTE kMaskSibScale = 0xC0; + static const BYTE kMaskSibIndex = 0x38; + static const BYTE kMaskSibBase = 0x07; + static const BYTE kSibBaseEbp = 0x05; + + int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr) + { + if (!aModRm) { + return -1; + } + int numBytes = 1; // Start with 1 for mod r/m byte itself + switch (*aModRm & kMaskMod) { + case kModReg: + return numBytes; + case kModDisp8: + numBytes += 1; + break; + case kModDisp32: + numBytes += 4; + break; + case kModNoRegDisp: + if ((*aModRm & kMaskRm) == kRmNoRegDispDisp32) { +#if defined(_M_X64) + // RIP-relative on AMD64, currently unsupported + return -1; +#else + // On IA-32, all ModR/M instruction modes address memory relative to 0 + numBytes += 4; +#endif + } else if (((*aModRm & kMaskRm) == kRmNeedSib && + (*(aModRm + 1) & kMaskSibBase) == kSibBaseEbp)) { + numBytes += 4; + } + break; + default: + // This should not be reachable + MOZ_ASSERT_UNREACHABLE("Impossible value for modr/m byte mod bits"); + return -1; + } + if ((*aModRm & kMaskRm) == kRmNeedSib) { + // SIB byte + numBytes += 1; + } + if (aSubOpcode) { + *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift; + } + return numBytes; + } + +#if defined(_M_X64) + // To patch for JMP and JE + + enum JumpType { + Je, + Jmp + }; + + struct JumpPatch { + JumpPatch() + : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp) + { + } + + JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp) + : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType) + { + } + + void AddJumpPatch(size_t aHookOffset, intptr_t aAbsJumpAddress, + JumpType aType = JumpType::Jmp) + { + mHookOffset = aHookOffset; + mJumpAddress = aAbsJumpAddress; + mType = aType; + } + + size_t GenerateJump(uint8_t* aCode) + { + size_t offset = mHookOffset; + if (mType == JumpType::Je) { + // JNE RIP+14 + aCode[offset] = 0x75; + aCode[offset + 1] = 14; + offset += 2; + } + + // JMP [RIP+0] + aCode[offset] = 0xff; + aCode[offset + 1] = 0x25; + *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0; + + // Jump table + *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress; + + return offset + 2 + 4 + 8; + } + + bool HasJumpPatch() const + { + return !!mJumpAddress; + } + + size_t mHookOffset; + intptr_t mJumpAddress; + JumpType mType; + }; + +#endif + + enum ePrefixGroupBits + { + eNoPrefixes = 0, + ePrefixGroup1 = (1 << 0), + ePrefixGroup2 = (1 << 1), + ePrefixGroup3 = (1 << 2), + ePrefixGroup4 = (1 << 3) + }; + + int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex, + unsigned char* aOutGroupBits) + { + unsigned char& groupBits = *aOutGroupBits; + groupBits = eNoPrefixes; + int index = aBytesIndex; + while (true) { + switch (aBytes[index]) { + // Group 1 + case 0xF0: // LOCK + case 0xF2: // REPNZ + case 0xF3: // REP / REPZ + if (groupBits & ePrefixGroup1) { + return -1; + } + groupBits |= ePrefixGroup1; + ++index; + break; + + // Group 2 + case 0x2E: // CS override / branch not taken + case 0x36: // SS override + case 0x3E: // DS override / branch taken + case 0x64: // FS override + case 0x65: // GS override + if (groupBits & ePrefixGroup2) { + return -1; + } + groupBits |= ePrefixGroup2; + ++index; + break; + + // Group 3 + case 0x66: // operand size override + if (groupBits & ePrefixGroup3) { + return -1; + } + groupBits |= ePrefixGroup3; + ++index; + break; + + // Group 4 + case 0x67: // Address size override + if (groupBits & ePrefixGroup4) { + return -1; + } + groupBits |= ePrefixGroup4; + ++index; + break; + + default: + return index - aBytesIndex; + } + } + } + + void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp) + { + *aOutTramp = nullptr; + + AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize, + PAGE_EXECUTE_READWRITE); + if (!protectHookPage.Protect()) { + return; + } + + byteptr_t tramp = FindTrampolineSpace(); + if (!tramp) { + return; + } + + byteptr_t origBytes = (byteptr_t)aOrigFunction; + + int nBytes = 0; + +#if defined(_M_IX86) + int pJmp32 = -1; + while (nBytes < 5) { + // Understand some simple instructions that might be found in a + // prologue; we might need to extend this as necessary. + // + // Note! If we ever need to understand jump instructions, we'll + // need to rewrite the displacement argument. + unsigned char prefixGroups; + int numPrefixBytes = CountPrefixBytes(origBytes, nBytes, &prefixGroups); + if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) { + // Either the prefix sequence was bad, or there are prefixes that + // we don't currently support (groups 3 and 4) + return; + } + nBytes += numPrefixBytes; + if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) { + // various MOVs + ++nBytes; + int len = CountModRmSib(origBytes + nBytes); + if (len < 0) { + return; + } + nBytes += len; + } else if (origBytes[nBytes] == 0xA1) { + // MOV eax, [seg:offset] + nBytes += 5; + } else if (origBytes[nBytes] == 0xB8) { + // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8 + nBytes += 5; + } else if (origBytes[nBytes] == 0x83) { + // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8 + unsigned char b = origBytes[nBytes + 1]; + if ((b & 0xc0) == 0xc0) { + // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8 + nBytes += 3; + } else { + // bail + return; + } + } else if (origBytes[nBytes] == 0x68) { + // PUSH with 4-byte operand + nBytes += 5; + } else if ((origBytes[nBytes] & 0xf0) == 0x50) { + // 1-byte PUSH/POP + nBytes++; + } else if (origBytes[nBytes] == 0x6A) { + // PUSH imm8 + nBytes += 2; + } else if (origBytes[nBytes] == 0xe9) { + pJmp32 = nBytes; + // jmp 32bit offset + nBytes += 5; + } else if (origBytes[nBytes] == 0xff && origBytes[nBytes + 1] == 0x25) { + // jmp [disp32] + nBytes += 6; + } else { + //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nBytes]); + return; + } + } +#elif defined(_M_X64) + JumpPatch jump; + + while (nBytes < 13) { + + // if found JMP 32bit offset, next bytes must be NOP or INT3 + if (jump.HasJumpPatch()) { + if (origBytes[nBytes] == 0x90 || origBytes[nBytes] == 0xcc) { + nBytes++; + continue; + } + return; + } + if (origBytes[nBytes] == 0x0f) { + nBytes++; + if (origBytes[nBytes] == 0x1f) { + // nop (multibyte) + nBytes++; + if ((origBytes[nBytes] & 0xc0) == 0x40 && + (origBytes[nBytes] & 0x7) == 0x04) { + nBytes += 3; + } else { + return; + } + } else if (origBytes[nBytes] == 0x05) { + // syscall + nBytes++; + } else if (origBytes[nBytes] == 0x84) { + // je rel32 + jump.AddJumpPatch(nBytes - 1, + (intptr_t) + origBytes + nBytes + 5 + + *(reinterpret_cast<int32_t*>(origBytes + + nBytes + 1)), + JumpType::Je); + nBytes += 5; + } else { + return; + } + } else if (origBytes[nBytes] == 0x40 || + origBytes[nBytes] == 0x41) { + // Plain REX or REX.B + nBytes++; + + if ((origBytes[nBytes] & 0xf0) == 0x50) { + // push/pop with Rx register + nBytes++; + } else if (origBytes[nBytes] >= 0xb8 && origBytes[nBytes] <= 0xbf) { + // mov r32, imm32 + nBytes += 5; + } else { + return; + } + } else if (origBytes[nBytes] == 0x45) { + // REX.R & REX.B + nBytes++; + + if (origBytes[nBytes] == 0x33) { + // xor r32, r32 + nBytes += 2; + } else { + return; + } + } else if ((origBytes[nBytes] & 0xfb) == 0x48) { + // REX.W | REX.WR + nBytes++; + + if (origBytes[nBytes] == 0x81 && + (origBytes[nBytes + 1] & 0xf8) == 0xe8) { + // sub r, dword + nBytes += 6; + } else if (origBytes[nBytes] == 0x83 && + (origBytes[nBytes + 1] & 0xf8) == 0xe8) { + // sub r, byte + nBytes += 3; + } else if (origBytes[nBytes] == 0x83 && + (origBytes[nBytes + 1] & 0xf8) == 0x60) { + // and [r+d], imm8 + nBytes += 5; + } else if (origBytes[nBytes] == 0x85) { + // 85 /r => TEST r/m32, r32 + if ((origBytes[nBytes + 1] & 0xc0) == 0xc0) { + nBytes += 2; + } else { + return; + } + } else if ((origBytes[nBytes] & 0xfd) == 0x89) { + ++nBytes; + // MOV r/m64, r64 | MOV r64, r/m64 + int len = CountModRmSib(origBytes + nBytes); + if (len < 0) { + return; + } + nBytes += len; + } else if (origBytes[nBytes] == 0xc7) { + // MOV r/m64, imm32 + if (origBytes[nBytes + 1] == 0x44) { + // MOV [r64+disp8], imm32 + // ModR/W + SIB + disp8 + imm32 + nBytes += 8; + } else { + return; + } + } else if (origBytes[nBytes] == 0xff) { + // JMP /4 + if ((origBytes[nBytes + 1] & 0xc0) == 0x0 && + (origBytes[nBytes + 1] & 0x07) == 0x5) { + // [rip+disp32] + // convert JMP 32bit offset to JMP 64bit direct + jump.AddJumpPatch(nBytes - 1, + *reinterpret_cast<intptr_t*>( + origBytes + nBytes + 6 + + *reinterpret_cast<int32_t*>(origBytes + nBytes + + 2))); + nBytes += 6; + } else { + // not support yet! + return; + } + } else { + // not support yet! + return; + } + } else if (origBytes[nBytes] == 0x66) { + // operand override prefix + nBytes += 1; + // This is the same as the x86 version + if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) { + // various MOVs + unsigned char b = origBytes[nBytes + 1]; + if (((b & 0xc0) == 0xc0) || + (((b & 0xc0) == 0x00) && + ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) { + // REG=r, R/M=r or REG=r, R/M=[r] + nBytes += 2; + } else if ((b & 0xc0) == 0x40) { + if ((b & 0x07) == 0x04) { + // REG=r, R/M=[SIB + disp8] + nBytes += 4; + } else { + // REG=r, R/M=[r + disp8] + nBytes += 3; + } + } else { + // complex MOV, bail + return; + } + } + } else if ((origBytes[nBytes] & 0xf0) == 0x50) { + // 1-byte push/pop + nBytes++; + } else if (origBytes[nBytes] == 0x65) { + // GS prefix + // + // The entry of GetKeyState on Windows 10 has the following code. + // 65 48 8b 04 25 30 00 00 00 mov rax,qword ptr gs:[30h] + // (GS prefix + REX + MOV (0x8b) ...) + if (origBytes[nBytes + 1] == 0x48 && + (origBytes[nBytes + 2] >= 0x88 && origBytes[nBytes + 2] <= 0x8b)) { + nBytes += 3; + int len = CountModRmSib(origBytes + nBytes); + if (len < 0) { + // no way to support this yet. + return; + } + nBytes += len; + } else { + return; + } + } else if (origBytes[nBytes] == 0x90) { + // nop + nBytes++; + } else if (origBytes[nBytes] == 0xb8) { + // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8 + nBytes += 5; + } else if (origBytes[nBytes] == 0x33) { + // xor r32, r/m32 + nBytes += 2; + } else if (origBytes[nBytes] == 0xf6) { + // test r/m8, imm8 (used by ntdll on Windows 10 x64) + // (no flags are affected by near jmp since there is no task switch, + // so it is ok for a jmp to be written immediately after a test) + BYTE subOpcode = 0; + int nModRmSibBytes = CountModRmSib(&origBytes[nBytes + 1], &subOpcode); + if (nModRmSibBytes < 0 || subOpcode != 0) { + // Unsupported + return; + } + nBytes += 2 + nModRmSibBytes; + } else if (origBytes[nBytes] == 0xc3) { + // ret + nBytes++; + } else if (origBytes[nBytes] == 0xcc) { + // int 3 + nBytes++; + } else if (origBytes[nBytes] == 0xe9) { + // jmp 32bit offset + jump.AddJumpPatch(nBytes, + // convert JMP 32bit offset to JMP 64bit direct + (intptr_t) + origBytes + nBytes + 5 + + *(reinterpret_cast<int32_t*>(origBytes + nBytes + 1))); + nBytes += 5; + } else if (origBytes[nBytes] == 0xff) { + nBytes++; + if ((origBytes[nBytes] & 0xf8) == 0xf0) { + // push r64 + nBytes++; + } else { + return; + } + } else { + return; + } + } +#else +#error "Unknown processor type" +#endif + + if (nBytes > 100) { + //printf ("Too big!"); + return; + } + + // We keep the address of the original function in the first bytes of + // the trampoline buffer + *((void**)tramp) = EncodePointer(aOrigFunction); + tramp += sizeof(void*); + + memcpy(tramp, aOrigFunction, nBytes); + + // OrigFunction+N, the target of the trampoline + byteptr_t trampDest = origBytes + nBytes; + +#if defined(_M_IX86) + if (pJmp32 >= 0) { + // Jump directly to the original target of the jump instead of jumping to the + // original function. + // Adjust jump target displacement to jump location in the trampoline. + *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp; + } else { + tramp[nBytes] = 0xE9; // jmp + *((intptr_t*)(tramp + nBytes + 1)) = + (intptr_t)trampDest - (intptr_t)(tramp + nBytes + 5); // target displacement + } +#elif defined(_M_X64) + // If JMP/JE opcode found, we don't insert to trampoline jump + if (jump.HasJumpPatch()) { + size_t offset = jump.GenerateJump(tramp); + if (jump.mType != JumpType::Jmp) { + JumpPatch patch(offset, reinterpret_cast<intptr_t>(trampDest)); + patch.GenerateJump(tramp); + } + } else { + JumpPatch patch(nBytes, reinterpret_cast<intptr_t>(trampDest)); + patch.GenerateJump(tramp); + } +#endif + + // The trampoline is now valid. + *aOutTramp = tramp; + + // ensure we can modify the original code + AutoVirtualProtect protect(aOrigFunction, nBytes, PAGE_EXECUTE_READWRITE); + if (!protect.Protect()) { + return; + } + +#if defined(_M_IX86) + // now modify the original bytes + origBytes[0] = 0xE9; // jmp + *((intptr_t*)(origBytes + 1)) = + aDest - (intptr_t)(origBytes + 5); // target displacement +#elif defined(_M_X64) + // mov r11, address + origBytes[0] = 0x49; + origBytes[1] = 0xbb; + + *((intptr_t*)(origBytes + 2)) = aDest; + + // jmp r11 + origBytes[10] = 0x41; + origBytes[11] = 0xff; + origBytes[12] = 0xe3; +#endif + } + + byteptr_t FindTrampolineSpace() + { + if (mCurHooks >= mMaxHooks) { + return 0; + } + + byteptr_t p = mHookPage + mCurHooks * kHookSize; + + mCurHooks++; + + return p; + } + + static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction) + { +#if defined(_M_IX86) + // If function entry is jmp [disp32] such as used by kernel32, + // we resolve redirected address from import table. + if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) { + return (void*)(**((uint32_t**) (aOriginalFunction + 2))); + } +#elif defined(_M_X64) + if (aOriginalFunction[0] == 0xe9) { + // require for TestDllInterceptor with --disable-optimize + int32_t offset = *((int32_t*)(aOriginalFunction + 1)); + return aOriginalFunction + 5 + offset; + } +#endif + + return aOriginalFunction; + } +}; + +} // namespace internal + +class WindowsDllInterceptor +{ + internal::WindowsDllNopSpacePatcher mNopSpacePatcher; + internal::WindowsDllDetourPatcher mDetourPatcher; + + const char* mModuleName; + int mNHooks; + +public: + WindowsDllInterceptor() + : mModuleName(nullptr) + , mNHooks(0) + {} + + void Init(const char* aModuleName, int aNumHooks = 0) + { + if (mModuleName) { + return; + } + + mModuleName = aModuleName; + mNHooks = aNumHooks; + mNopSpacePatcher.Init(aModuleName); + + // Lazily initialize mDetourPatcher, since it allocates memory and we might + // not need it. + } + + bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc) + { + // Use a nop space patch if possible, otherwise fall back to a detour. + // This should be the preferred method for adding hooks. + + if (!mModuleName) { + return false; + } + + if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) { + return true; + } + + return AddDetour(aName, aHookDest, aOrigFunc); + } + + bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc) + { + // Generally, code should not call this method directly. Use AddHook unless + // there is a specific need to avoid nop space patches. + + if (!mModuleName) { + return false; + } + + if (!mDetourPatcher.Initialized()) { + mDetourPatcher.Init(mModuleName, mNHooks); + } + + return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc); + } +}; + +} // namespace mozilla + +#endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */ diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h new file mode 100644 index 000000000..925ca6901 --- /dev/null +++ b/xpcom/build/nsXPCOM.h @@ -0,0 +1,433 @@ +/* -*- 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/. */ + +#ifndef nsXPCOM_h__ +#define nsXPCOM_h__ + +#include "nscore.h" +#include "nsXPCOMCID.h" + +#ifdef __cplusplus +#define DECL_CLASS(c) class c +#define DECL_STRUCT(c) struct c +#else +#define DECL_CLASS(c) typedef struct c c +#define DECL_STRUCT(c) typedef struct c c +#endif + +DECL_CLASS(nsAString); +DECL_CLASS(nsACString); + +DECL_CLASS(nsISupports); +DECL_CLASS(nsIModule); +DECL_CLASS(nsIComponentManager); +DECL_CLASS(nsIComponentRegistrar); +DECL_CLASS(nsIServiceManager); +DECL_CLASS(nsIFile); +DECL_CLASS(nsILocalFile); +DECL_CLASS(nsIDirectoryServiceProvider); +DECL_CLASS(nsIMemory); +DECL_CLASS(nsIDebug2); + +#ifdef __cplusplus +namespace mozilla { +struct Module; +} // namespace mozilla +#endif + +/** + * Initialises XPCOM. You must call one of the NS_InitXPCOM methods + * before proceeding to use xpcom. The one exception is that you may + * call NS_NewLocalFile to create a nsIFile. + * + * @note Use <CODE>NS_NewLocalFile</CODE> or <CODE>NS_NewNativeLocalFile</CODE> + * to create the file object you supply as the bin directory path in this + * call. The function may be safely called before the rest of XPCOM or + * embedding has been initialised. + * + * @param aResult The service manager. You may pass null. + * + * @param aBinDirectory The directory containing the component + * registry and runtime libraries; + * or use <CODE>nullptr</CODE> to use the working + * directory. + * + * @param aAppFileLocationProvider The object to be used by Gecko that + * specifies to Gecko where to find profiles, the + * component registry preferences and so on; or use + * <CODE>nullptr</CODE> for the default behaviour. + * + * @see NS_NewLocalFile + * @see nsIFile + * @see nsIDirectoryServiceProvider + * + * @return NS_OK for success; + * NS_ERROR_NOT_INITIALIZED if static globals were not initialized, + * which can happen if XPCOM is reloaded, but did not completly + * shutdown. Other error codes indicate a failure during + * initialisation. + */ +XPCOM_API(nsresult) +NS_InitXPCOM2(nsIServiceManager** aResult, + nsIFile* aBinDirectory, + nsIDirectoryServiceProvider* aAppFileLocationProvider); + +/** + * Initialize only minimal components of XPCOM. This ensures nsThreadManager, + * logging, and timers will work. + */ +XPCOM_API(nsresult) +NS_InitMinimalXPCOM(); + +/** + * Shutdown XPCOM. You must call this method after you are finished + * using xpcom. + * + * @param aServMgr The service manager which was returned by NS_InitXPCOM. + * This will release servMgr. You may pass null. + * + * @return NS_OK for success; + * other error codes indicate a failure during initialisation. + */ +XPCOM_API(nsresult) NS_ShutdownXPCOM(nsIServiceManager* aServMgr); + + +/** + * Public Method to access to the service manager. + * + * @param aResult Interface pointer to the service manager + * + * @return NS_OK for success; + * other error codes indicate a failure during initialisation. + */ +XPCOM_API(nsresult) NS_GetServiceManager(nsIServiceManager** aResult); + +/** + * Public Method to access to the component manager. + * + * @param aResult Interface pointer to the service + * + * @return NS_OK for success; + * other error codes indicate a failure during initialisation. + */ +XPCOM_API(nsresult) NS_GetComponentManager(nsIComponentManager** aResult); + + +/** + * Public Method to access to the component registration manager. + * + * @param aResult Interface pointer to the service + * + * @return NS_OK for success; + * other error codes indicate a failure during initialisation. + */ +XPCOM_API(nsresult) NS_GetComponentRegistrar(nsIComponentRegistrar** aResult); + +/** + * Public Method to access to the memory manager. See nsIMemory + * + * @param aResult Interface pointer to the memory manager + * + * @return NS_OK for success; + * other error codes indicate a failure during initialisation. + */ +XPCOM_API(nsresult) NS_GetMemoryManager(nsIMemory** aResult); + +/** + * Public Method to create an instance of a nsIFile. This function + * may be called prior to NS_InitXPCOM. + * + * @param aPath + * A string which specifies a full file path to a + * location. Relative paths will be treated as an + * error (NS_ERROR_FILE_UNRECOGNIZED_PATH). + * |NS_NewNativeLocalFile|'s path must be in the + * filesystem charset. + * @param aFollowLinks + * This attribute will determine if the nsLocalFile will auto + * resolve symbolic links. By default, this value will be false + * on all non unix systems. On unix, this attribute is effectively + * a noop. + * @param aResult Interface pointer to a new instance of an nsIFile + * + * @return NS_OK for success; + * other error codes indicate a failure. + */ + +#ifdef __cplusplus + +XPCOM_API(nsresult) NS_NewLocalFile(const nsAString& aPath, + bool aFollowLinks, + nsIFile** aResult); + +XPCOM_API(nsresult) NS_NewNativeLocalFile(const nsACString& aPath, + bool aFollowLinks, + nsIFile** aResult); + +#endif + +/** + * Allocator functions for the standalone glue. + * Do not use outside the xpcom glue code. + * Use moz_xmalloc/moz_xrealloc/free, or new/delete instead. + */ +#ifdef XPCOM_GLUE +/** + * Allocates a block of memory of a particular size. If the memory cannot + * be allocated (because of an out-of-memory condition), the process aborts. + * + * @param aSize The size of the block to allocate + * @result The block of memory + * @note This function is thread-safe. + */ +XPCOM_API(void*) NS_Alloc(size_t aSize); + +/** + * Reallocates a block of memory to a new size. + * + * @param aPtr The block of memory to reallocate. This block must originally + have been allocated by NS_Alloc or NS_Realloc + * @param aSize The new size. If 0, frees the block like NS_Free + * @result The reallocated block of memory + * @note This function is thread-safe. + * + * If aPtr is null, this function behaves like NS_Alloc. + * If s is the size of the block to which aPtr points, the first min(s, size) + * bytes of aPtr's block are copied to the new block. If the allocation + * succeeds, aPtr is freed and a pointer to the new block is returned. If the + * allocation fails, the process aborts. + */ +XPCOM_API(void*) NS_Realloc(void* aPtr, size_t aSize); + +/** + * Frees a block of memory. Null is a permissible value, in which case no + * action is taken. + * + * @param aPtr The block of memory to free. This block must originally have + * been allocated by NS_Alloc or NS_Realloc + * @note This function is thread-safe. + */ +XPCOM_API(void) NS_Free(void* aPtr); +#else +#define NS_Alloc moz_xmalloc +#define NS_Realloc moz_xrealloc +#define NS_Free free +#endif + +/** + * Support for warnings, assertions, and debugging breaks. + */ + +enum +{ + NS_DEBUG_WARNING = 0, + NS_DEBUG_ASSERTION = 1, + NS_DEBUG_BREAK = 2, + NS_DEBUG_ABORT = 3 +}; + +/** + * Print a runtime assertion. This function is available in both debug and + * release builds. + * + * @note Based on the value of aSeverity and the XPCOM_DEBUG_BREAK + * environment variable, this function may cause the application to + * print the warning, print a stacktrace, break into a debugger, or abort + * immediately. + * + * @param aSeverity A NS_DEBUG_* value + * @param aStr A readable error message (ASCII, may be null) + * @param aExpr The expression evaluated (may be null) + * @param aFile The source file containing the assertion (may be null) + * @param aLine The source file line number (-1 indicates no line number) + */ +XPCOM_API(void) NS_DebugBreak(uint32_t aSeverity, + const char* aStr, const char* aExpr, + const char* aFile, int32_t aLine); + +/** + * Perform a stack-walk to a debugging log under various + * circumstances. Used to aid debugging of leaked object graphs. + * + * The NS_Log* functions are available in both debug and release + * builds of XPCOM, but the output will be useless unless binary + * debugging symbols for all modules in the stacktrace are available. + */ + +/** + * By default, refcount logging is enabled at NS_InitXPCOM and + * refcount statistics are printed at NS_ShutdownXPCOM. NS_LogInit and + * NS_LogTerm allow applications to enable logging earlier and delay + * printing of logging statistics. They should always be used as a + * matched pair. + */ +XPCOM_API(void) NS_LogInit(); + +XPCOM_API(void) NS_LogTerm(); + +#ifdef __cplusplus +/** + * A helper class that calls NS_LogInit in its constructor and + * NS_LogTerm in its destructor. + */ + +class ScopedLogging +{ +public: + ScopedLogging() + { + NS_LogInit(); + } + + ~ScopedLogging() + { + NS_LogTerm(); + } +}; +#endif + +/** + * Log construction and destruction of objects. Processing tools can use the + * stacktraces printed by these functions to identify objects that are being + * leaked. + * + * @param aPtr A pointer to the concrete object. + * @param aTypeName The class name of the type + * @param aInstanceSize The size of the type + */ + +XPCOM_API(void) NS_LogCtor(void* aPtr, const char* aTypeName, + uint32_t aInstanceSize); + +XPCOM_API(void) NS_LogDtor(void* aPtr, const char* aTypeName, + uint32_t aInstanceSize); + +/** + * Log a stacktrace when an XPCOM object's refcount is incremented or + * decremented. Processing tools can use the stacktraces printed by these + * functions to identify objects that were leaked due to XPCOM references. + * + * @param aPtr A pointer to the concrete object + * @param aNewRefCnt The new reference count. + * @param aTypeName The class name of the type + * @param aInstanceSize The size of the type + */ +XPCOM_API(void) NS_LogAddRef(void* aPtr, nsrefcnt aNewRefCnt, + const char* aTypeName, uint32_t aInstanceSize); + +XPCOM_API(void) NS_LogRelease(void* aPtr, nsrefcnt aNewRefCnt, + const char* aTypeName); + +/** + * Log reference counting performed by COMPtrs. Processing tools can + * use the stacktraces printed by these functions to simplify reports + * about leaked objects generated from the data printed by + * NS_LogAddRef/NS_LogRelease. + * + * @param aCOMPtr the address of the COMPtr holding a strong reference + * @param aObject the object being referenced by the COMPtr + */ + +XPCOM_API(void) NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject); + +XPCOM_API(void) NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject); + +/** + * The XPCOM cycle collector analyzes and breaks reference cycles between + * participating XPCOM objects. All objects in the cycle must implement + * nsCycleCollectionParticipant to break cycles correctly. + */ + +#ifdef __cplusplus + +class nsCycleCollectionParticipant; +class nsCycleCollectingAutoRefCnt; + +XPCOM_API(void) NS_CycleCollectorSuspect3(void* aPtr, + nsCycleCollectionParticipant* aCp, + nsCycleCollectingAutoRefCnt* aRefCnt, + bool* aShouldDelete); + +#endif + +/** + * Categories (in the category manager service) used by XPCOM: + */ + +/** + * A category which is read after component registration but before + * the "xpcom-startup" notifications. Each category entry is treated + * as the contract ID of a service which implements + * nsIDirectoryServiceProvider. Each directory service provider is + * installed in the global directory service. + */ +#define XPCOM_DIRECTORY_PROVIDER_CATEGORY "xpcom-directory-providers" + +/** + * A category which is read after component registration but before + * NS_InitXPCOM returns. Each category entry is treated as the contractID of + * a service: each service is instantiated, and if it implements nsIObserver + * the nsIObserver.observe method is called with the "xpcom-startup" topic. + */ +#define NS_XPCOM_STARTUP_CATEGORY "xpcom-startup" + + +/** + * Observer topics (in the observer service) used by XPCOM: + */ + +/** + * At XPCOM startup after component registration is complete, the + * following topic is notified. In order to receive this notification, + * component must register their contract ID in the category manager, + * + * @see NS_XPCOM_STARTUP_CATEGORY + */ +#define NS_XPCOM_STARTUP_OBSERVER_ID "xpcom-startup" + +/** + * At XPCOM shutdown, this topic is notified just before "xpcom-shutdown". + * Components should only use this to mark themselves as 'being destroyed'. + * Nothing should be dispatched to any event loop. + */ +#define NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID "xpcom-will-shutdown" + +/** + * At XPCOM shutdown, this topic is notified. All components must + * release any interface references to objects in other modules when + * this topic is notified. + */ +#define NS_XPCOM_SHUTDOWN_OBSERVER_ID "xpcom-shutdown" + +/** + * This topic is notified when an entry was added to a category in the + * category manager. The subject of the notification will be the name of + * the added entry as an nsISupportsCString, and the data will be the + * name of the category. The notification will occur on the main thread. + */ +#define NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID \ + "xpcom-category-entry-added" + +/** + * This topic is notified when an entry was removed from a category in the + * category manager. The subject of the notification will be the name of + * the removed entry as an nsISupportsCString, and the data will be the + * name of the category. The notification will occur on the main thread. + */ +#define NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID \ + "xpcom-category-entry-removed" + +/** + * This topic is notified when an a category was cleared in the category + * manager. The subject of the notification will be the category manager, + * and the data will be the name of the cleared category. + * The notification will occur on the main thread. + */ +#define NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID "xpcom-category-cleared" + +XPCOM_API(nsresult) NS_GetDebug(nsIDebug2** aResult); + +#endif diff --git a/xpcom/build/nsXPCOMCID.h b/xpcom/build/nsXPCOMCID.h new file mode 100644 index 000000000..7219babe4 --- /dev/null +++ b/xpcom/build/nsXPCOMCID.h @@ -0,0 +1,185 @@ +/* -*- 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/. */ + +#ifndef nsXPCOMCID_h__ +#define nsXPCOMCID_h__ + +/** + * XPCOM Directory Service Contract ID + * The directory service provides ways to obtain file system locations. The + * directory service is a singleton. + * + * This contract supports the nsIDirectoryService and the nsIProperties + * interfaces. + * + */ +#define NS_DIRECTORY_SERVICE_CONTRACTID "@mozilla.org/file/directory_service;1" + +/** + * XPCOM File + * The file abstraction provides ways to obtain and access files and + * directories located on the local system. + * + * This contract supports the nsIFile interface. + * This contract may also support platform specific interfaces such as + * nsILocalFileMac on platforms where additional interfaces are required. + * + */ +#define NS_LOCAL_FILE_CONTRACTID "@mozilla.org/file/local;1" + +/** + * XPCOM Category Manager Contract ID + * The contract supports the nsICategoryManager interface. The + * category manager is a singleton. + * The "enumerateCategory" method of nsICategoryManager will return an object + * that implements nsIUTF8StringEnumerator. In addition, the enumerator will + * return the entries in sorted order (sorted by byte comparison). + */ +#define NS_CATEGORYMANAGER_CONTRACTID "@mozilla.org/categorymanager;1" + +/** + * XPCOM Properties Object Contract ID + * Simple mapping object which supports the nsIProperties interface. + */ +#define NS_PROPERTIES_CONTRACTID "@mozilla.org/properties;1" + +/** + * XPCOM Array Object ContractID + * Simple array implementation which supports the nsIArray and + * nsIMutableArray interfaces. + */ +#define NS_ARRAY_CONTRACTID "@mozilla.org/array;1" + +/** + * Observer Service ContractID + * The observer service implements the global nsIObserverService object. + * It should be used from the main thread only. + */ +#define NS_OBSERVERSERVICE_CONTRACTID "@mozilla.org/observer-service;1" + +/** + * IO utilities service contract id. + * This guarantees implementation of nsIIOUtil. Usable from any thread. + */ +#define NS_IOUTIL_CONTRACTID "@mozilla.org/io-util;1" + +/** + * Memory reporter service CID + */ +#define NS_MEMORY_REPORTER_MANAGER_CONTRACTID "@mozilla.org/memory-reporter-manager;1" + +/** + * Memory info dumper service CID + */ +#define NS_MEMORY_INFO_DUMPER_CONTRACTID "@mozilla.org/memory-info-dumper;1" + +/** + * Status reporter service CID + */ +#define NS_STATUS_REPORTER_MANAGER_CONTRACTID "@mozilla.org/status-reporter-manager;1" + +/** + * Cycle collector logger contract id + */ +#define NS_CYCLE_COLLECTOR_LOGGER_CONTRACTID "@mozilla.org/cycle-collector-logger;1" + +/** + * nsMessageLoop contract id + */ +#define NS_MESSAGE_LOOP_CONTRACTID "@mozilla.org/message-loop;1" + +#define NS_COMPARTMENT_INFO_CONTRACTID "@mozilla.org/compartment-info;1" + +/** + * The following are the CIDs and Contract IDs of the nsISupports wrappers for + * primative types. + */ +#define NS_SUPPORTS_ID_CID \ +{ 0xacf8dc40, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_ID_CONTRACTID "@mozilla.org/supports-id;1" + +#define NS_SUPPORTS_CSTRING_CID \ +{ 0xacf8dc41, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_CSTRING_CONTRACTID "@mozilla.org/supports-cstring;1" + +#define NS_SUPPORTS_STRING_CID \ +{ 0xacf8dc42, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_STRING_CONTRACTID "@mozilla.org/supports-string;1" + +#define NS_SUPPORTS_PRBOOL_CID \ +{ 0xacf8dc43, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRBOOL_CONTRACTID "@mozilla.org/supports-PRBool;1" + +#define NS_SUPPORTS_PRUINT8_CID \ +{ 0xacf8dc44, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRUINT8_CONTRACTID "@mozilla.org/supports-PRUint8;1" + +#define NS_SUPPORTS_PRUINT16_CID \ +{ 0xacf8dc46, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRUINT16_CONTRACTID "@mozilla.org/supports-PRUint16;1" + +#define NS_SUPPORTS_PRUINT32_CID \ +{ 0xacf8dc47, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRUINT32_CONTRACTID "@mozilla.org/supports-PRUint32;1" + +#define NS_SUPPORTS_PRUINT64_CID \ +{ 0xacf8dc48, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRUINT64_CONTRACTID "@mozilla.org/supports-PRUint64;1" + +#define NS_SUPPORTS_PRTIME_CID \ +{ 0xacf8dc49, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRTIME_CONTRACTID "@mozilla.org/supports-PRTime;1" + +#define NS_SUPPORTS_CHAR_CID \ +{ 0xacf8dc4a, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_CHAR_CONTRACTID "@mozilla.org/supports-char;1" + +#define NS_SUPPORTS_PRINT16_CID \ +{ 0xacf8dc4b, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRINT16_CONTRACTID "@mozilla.org/supports-PRInt16;1" + +#define NS_SUPPORTS_PRINT32_CID \ +{ 0xacf8dc4c, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRINT32_CONTRACTID "@mozilla.org/supports-PRInt32;1" + +#define NS_SUPPORTS_PRINT64_CID \ +{ 0xacf8dc4d, 0x4a25, 0x11d3, \ +{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } } +#define NS_SUPPORTS_PRINT64_CONTRACTID "@mozilla.org/supports-PRInt64;1" + +#define NS_SUPPORTS_FLOAT_CID \ +{ 0xcbf86870, 0x4ac0, 0x11d3, \ +{ 0xba, 0xea, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } +#define NS_SUPPORTS_FLOAT_CONTRACTID "@mozilla.org/supports-float;1" + +#define NS_SUPPORTS_DOUBLE_CID \ +{ 0xcbf86871, 0x4ac0, 0x11d3, \ +{ 0xba, 0xea, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } +#define NS_SUPPORTS_DOUBLE_CONTRACTID "@mozilla.org/supports-double;1" + +#define NS_SUPPORTS_VOID_CID \ +{ 0xaf10f3e0, 0x568d, 0x11d3, \ +{ 0xba, 0xf8, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } +#define NS_SUPPORTS_VOID_CONTRACTID "@mozilla.org/supports-void;1" + +#define NS_SUPPORTS_INTERFACE_POINTER_CID \ +{ 0xA99FEBBA, 0x1DD1, 0x11B2, \ +{ 0xA9, 0x43, 0xB0, 0x23, 0x34, 0xA6, 0xD0, 0x83 } } +#define NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID "@mozilla.org/supports-interface-pointer;1" + +#endif diff --git a/xpcom/build/nsXPCOMCIDInternal.h b/xpcom/build/nsXPCOMCIDInternal.h new file mode 100644 index 000000000..c9f7fb6f3 --- /dev/null +++ b/xpcom/build/nsXPCOMCIDInternal.h @@ -0,0 +1,54 @@ +/* -*- 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/. */ + +#ifndef nsXPCOMCIDInternal_h__ +#define nsXPCOMCIDInternal_h__ + +#include "nsXPCOMCID.h" + +/** + * A hashtable-based property bag component. + * @implements nsIWritablePropertyBag, nsIWritablePropertyBag2 + */ +#define NS_HASH_PROPERTY_BAG_CID \ +{ 0x678c50b8, 0x6bcb, 0x4ad0, \ +{ 0xb9, 0xb8, 0xc8, 0x11, 0x75, 0x95, 0x51, 0x99 } } +#define NS_HASH_PROPERTY_BAG_CONTRACTID "@mozilla.org/hash-property-bag;1" + +/** + * Factory for creating nsIUnicharInputStream + * @implements nsIUnicharInputStreamFactory + * @note nsIUnicharInputStream instances cannot be created via + * createInstance. Code must use one of the custom factory methods. + */ +#define NS_SIMPLE_UNICHAR_STREAM_FACTORY_CONTRACTID \ + "@mozilla.org/xpcom/simple-unichar-stream-factory;1" + +/** + * The global thread manager service. This component is a singleton. + * @implements nsIThreadManager + */ +#define NS_THREADMANAGER_CONTRACTID "@mozilla.org/thread-manager;1" + +/** + * A thread pool component. + * @implements nsIThreadPool + */ +#define NS_THREADPOOL_CONTRACTID "@mozilla.org/thread-pool;1" + +/** + * The contract id for the nsIXULAppInfo service. + */ +#define XULAPPINFO_SERVICE_CONTRACTID \ + "@mozilla.org/xre/app-info;1" + +/** + * The contract id for the nsIXULRuntime service. + */ +#define XULRUNTIME_SERVICE_CONTRACTID \ + "@mozilla.org/xre/runtime;1" + +#endif // nsXPCOMCIDInternal_h__ diff --git a/xpcom/build/nsXPCOMPrivate.h b/xpcom/build/nsXPCOMPrivate.h new file mode 100644 index 000000000..99a994013 --- /dev/null +++ b/xpcom/build/nsXPCOMPrivate.h @@ -0,0 +1,317 @@ +/* -*- 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/. */ + +#ifndef nsXPCOMPrivate_h__ +#define nsXPCOMPrivate_h__ + +#include "nscore.h" +#include "nsXPCOM.h" +#include "nsXPCOMStrings.h" +#include "xptcall.h" + +class nsStringContainer; +class nsCStringContainer; +class nsPurpleBufferEntry; + +/** + * During this shutdown notification all threads which run XPCOM code must + * be joined. + */ +#define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID "xpcom-shutdown-threads" + +/** + * During this shutdown notification all module loaders must unload XPCOM + * modules. + */ +#define NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID "xpcom-shutdown-loaders" + +// PUBLIC +typedef nsresult (*InitFunc)(nsIServiceManager** aResult, + nsIFile* aBinDirectory, + nsIDirectoryServiceProvider* aAppFileLocationProvider); +typedef nsresult (*ShutdownFunc)(nsIServiceManager* aServMgr); +typedef nsresult (*GetServiceManagerFunc)(nsIServiceManager** aResult); +typedef nsresult (*GetComponentManagerFunc)(nsIComponentManager** aResult); +typedef nsresult (*GetComponentRegistrarFunc)(nsIComponentRegistrar** aResult); +typedef nsresult (*GetMemoryManagerFunc)(nsIMemory** aResult); +typedef nsresult (*NewLocalFileFunc)(const nsAString& aPath, + bool aFollowLinks, nsIFile** aResult); +typedef nsresult (*NewNativeLocalFileFunc)(const nsACString& aPath, + bool aFollowLinks, + nsIFile** aResult); + +typedef nsresult (*GetDebugFunc)(nsIDebug2** aResult); + +typedef nsresult (*StringContainerInitFunc)(nsStringContainer&); +typedef nsresult (*StringContainerInit2Func)(nsStringContainer&, + const char16_t*, + uint32_t, uint32_t); +typedef void (*StringContainerFinishFunc)(nsStringContainer&); +typedef uint32_t (*StringGetDataFunc)(const nsAString&, const char16_t**, + bool*); +typedef uint32_t (*StringGetMutableDataFunc)(nsAString&, uint32_t, + char16_t**); +typedef char16_t* (*StringCloneDataFunc)(const nsAString&); +typedef nsresult (*StringSetDataFunc)(nsAString&, const char16_t*, uint32_t); +typedef nsresult (*StringSetDataRangeFunc)(nsAString&, uint32_t, uint32_t, + const char16_t*, uint32_t); +typedef nsresult (*StringCopyFunc)(nsAString&, const nsAString&); +typedef void (*StringSetIsVoidFunc)(nsAString&, const bool); +typedef bool (*StringGetIsVoidFunc)(const nsAString&); + +typedef nsresult (*CStringContainerInitFunc)(nsCStringContainer&); +typedef nsresult (*CStringContainerInit2Func)(nsCStringContainer&, + const char*, + uint32_t, uint32_t); +typedef void (*CStringContainerFinishFunc)(nsCStringContainer&); +typedef uint32_t (*CStringGetDataFunc)(const nsACString&, const char**, + bool*); +typedef uint32_t (*CStringGetMutableDataFunc)(nsACString&, uint32_t, char**); +typedef char* (*CStringCloneDataFunc)(const nsACString&); +typedef nsresult (*CStringSetDataFunc)(nsACString&, const char*, uint32_t); +typedef nsresult (*CStringSetDataRangeFunc)(nsACString&, uint32_t, uint32_t, + const char*, uint32_t); +typedef nsresult (*CStringCopyFunc)(nsACString&, const nsACString&); +typedef void (*CStringSetIsVoidFunc)(nsACString&, const bool); +typedef bool (*CStringGetIsVoidFunc)(const nsACString&); + +typedef nsresult (*CStringToUTF16)(const nsACString&, nsCStringEncoding, + nsAString&); +typedef nsresult (*UTF16ToCString)(const nsAString&, nsCStringEncoding, + nsACString&); + +typedef void* (*AllocFunc)(size_t aSize); +typedef void* (*ReallocFunc)(void* aPtr, size_t aSize); +typedef void (*FreeFunc)(void* aPtr); + +typedef void (*DebugBreakFunc)(uint32_t aSeverity, + const char* aStr, const char* aExpr, + const char* aFile, int32_t aLine); + +typedef void (*xpcomVoidFunc)(); +typedef void (*LogAddRefFunc)(void*, nsrefcnt, const char*, uint32_t); +typedef void (*LogReleaseFunc)(void*, nsrefcnt, const char*); +typedef void (*LogCtorFunc)(void*, const char*, uint32_t); +typedef void (*LogCOMPtrFunc)(void*, nsISupports*); + +typedef nsresult (*GetXPTCallStubFunc)(REFNSIID, nsIXPTCProxy*, + nsISomeInterface**); +typedef void (*DestroyXPTCallStubFunc)(nsISomeInterface*); +typedef nsresult (*InvokeByIndexFunc)(nsISupports*, uint32_t, uint32_t, + nsXPTCVariant*); +typedef bool (*CycleCollectorFunc)(nsISupports*); +typedef nsPurpleBufferEntry* + (*CycleCollectorSuspect2Func)(void*, + nsCycleCollectionParticipant*); +typedef bool (*CycleCollectorForget2Func)(nsPurpleBufferEntry*); +typedef void (*CycleCollectorSuspect3Func)(void*, + nsCycleCollectionParticipant*, + nsCycleCollectingAutoRefCnt*, + bool*); +// PRIVATE AND DEPRECATED +typedef NS_CALLBACK(XPCOMExitRoutine)(void); + +typedef nsresult (*RegisterXPCOMExitRoutineFunc)(XPCOMExitRoutine aExitRoutine, + uint32_t aPriority); +typedef nsresult (*UnregisterXPCOMExitRoutineFunc)(XPCOMExitRoutine aExitRoutine); + +typedef struct XPCOMFunctions +{ + uint32_t version; + uint32_t size; + + InitFunc init; + ShutdownFunc shutdown; + GetServiceManagerFunc getServiceManager; + GetComponentManagerFunc getComponentManager; + GetComponentRegistrarFunc getComponentRegistrar; + GetMemoryManagerFunc getMemoryManager; + NewLocalFileFunc newLocalFile; + NewNativeLocalFileFunc newNativeLocalFile; + + RegisterXPCOMExitRoutineFunc registerExitRoutine; + UnregisterXPCOMExitRoutineFunc unregisterExitRoutine; + + // Added for Mozilla 1.5 + GetDebugFunc getDebug; + void* getTraceRefcnt; + + // Added for Mozilla 1.7 + StringContainerInitFunc stringContainerInit; + StringContainerFinishFunc stringContainerFinish; + StringGetDataFunc stringGetData; + StringSetDataFunc stringSetData; + StringSetDataRangeFunc stringSetDataRange; + StringCopyFunc stringCopy; + CStringContainerInitFunc cstringContainerInit; + CStringContainerFinishFunc cstringContainerFinish; + CStringGetDataFunc cstringGetData; + CStringSetDataFunc cstringSetData; + CStringSetDataRangeFunc cstringSetDataRange; + CStringCopyFunc cstringCopy; + CStringToUTF16 cstringToUTF16; + UTF16ToCString utf16ToCString; + StringCloneDataFunc stringCloneData; + CStringCloneDataFunc cstringCloneData; + + // Added for Mozilla 1.8 + AllocFunc allocFunc; + ReallocFunc reallocFunc; + FreeFunc freeFunc; + StringContainerInit2Func stringContainerInit2; + CStringContainerInit2Func cstringContainerInit2; + StringGetMutableDataFunc stringGetMutableData; + CStringGetMutableDataFunc cstringGetMutableData; + void* init3; // obsolete + + // Added for Mozilla 1.9 + DebugBreakFunc debugBreakFunc; + xpcomVoidFunc logInitFunc; + xpcomVoidFunc logTermFunc; + LogAddRefFunc logAddRefFunc; + LogReleaseFunc logReleaseFunc; + LogCtorFunc logCtorFunc; + LogCtorFunc logDtorFunc; + LogCOMPtrFunc logCOMPtrAddRefFunc; + LogCOMPtrFunc logCOMPtrReleaseFunc; + GetXPTCallStubFunc getXPTCallStubFunc; + DestroyXPTCallStubFunc destroyXPTCallStubFunc; + InvokeByIndexFunc invokeByIndexFunc; + CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect3Func + CycleCollectorFunc cycleForgetFunc; // obsolete + StringSetIsVoidFunc stringSetIsVoid; + StringGetIsVoidFunc stringGetIsVoid; + CStringSetIsVoidFunc cstringSetIsVoid; + CStringGetIsVoidFunc cstringGetIsVoid; + + // Added for Mozilla 1.9.1 + CycleCollectorSuspect2Func cycleSuspect2Func; // obsolete: use cycleSuspect3Func + CycleCollectorForget2Func cycleForget2Func; // obsolete + + CycleCollectorSuspect3Func cycleSuspect3Func; + +} XPCOMFunctions; + +typedef nsresult (*GetFrozenFunctionsFunc)(XPCOMFunctions* aEntryPoints, + const char* aLibraryPath); +XPCOM_API(nsresult) NS_GetFrozenFunctions(XPCOMFunctions* aEntryPoints, + const char* aLibraryPath); + + +namespace mozilla { + +/** + * Shutdown XPCOM. You must call this method after you are finished + * using xpcom. + * + * @param aServMgr The service manager which was returned by NS_InitXPCOM. + * This will release servMgr. You may pass null. + * + * @return NS_OK for success; + * other error codes indicate a failure during shutdown + * + */ +nsresult +ShutdownXPCOM(nsIServiceManager* aServMgr); + +void SetICUMemoryFunctions(); + +/** + * C++ namespaced version of NS_LogTerm. + */ +void LogTerm(); + +} // namespace mozilla + + +// think hard before changing this +#define XPCOM_GLUE_VERSION 1 + + +/* XPCOM Specific Defines + * + * XPCOM_DLL - name of the loadable xpcom library on disk. + * XUL_DLL - name of the loadable XUL library on disk + * XPCOM_SEARCH_KEY - name of the environment variable that can be + * modified to include additional search paths. + * GRE_CONF_NAME - Name of the GRE Configuration file + */ + +#if defined(XP_WIN32) + +#define XPCOM_SEARCH_KEY "PATH" +#define GRE_CONF_NAME "gre.config" +#define GRE_WIN_REG_LOC L"Software\\mozilla.org\\GRE" +#define XPCOM_DLL XUL_DLL +#define LXPCOM_DLL LXUL_DLL +#define XUL_DLL "xul.dll" +#define LXUL_DLL L"xul.dll" + +#else // Unix +#include <limits.h> // for PATH_MAX + +#define XPCOM_DLL XUL_DLL + +// you have to love apple.. +#ifdef XP_MACOSX +#define XPCOM_SEARCH_KEY "DYLD_LIBRARY_PATH" +#define GRE_FRAMEWORK_NAME "XUL.framework" +#define XUL_DLL "XUL" +#else +#define XPCOM_SEARCH_KEY "LD_LIBRARY_PATH" +#define XUL_DLL "libxul" MOZ_DLL_SUFFIX +#endif + +#define GRE_CONF_NAME ".gre.config" +#define GRE_CONF_PATH "/etc/gre.conf" +#define GRE_CONF_DIR "/etc/gre.d" +#define GRE_USER_CONF_DIR ".gre.d" +#endif + +#if defined(XP_WIN) + #define XPCOM_FILE_PATH_SEPARATOR "\\" + #define XPCOM_ENV_PATH_SEPARATOR ";" +#elif defined(XP_UNIX) + #define XPCOM_FILE_PATH_SEPARATOR "/" + #define XPCOM_ENV_PATH_SEPARATOR ":" +#else + #error need_to_define_your_file_path_separator_and_illegal_characters +#endif + +#ifdef AIX +#include <sys/param.h> +#endif + +#ifndef MAXPATHLEN +#ifdef PATH_MAX +#define MAXPATHLEN PATH_MAX +#elif defined(_MAX_PATH) +#define MAXPATHLEN _MAX_PATH +#elif defined(CCHMAXPATH) +#define MAXPATHLEN CCHMAXPATH +#else +#define MAXPATHLEN 1024 +#endif +#endif + +extern bool gXPCOMShuttingDown; +extern bool gXPCOMThreadsShutDown; + +// Needed by the IPC layer from off the main thread +extern char16_t* gGREBinPath; + +namespace mozilla { +namespace services { + +/** + * Clears service cache, sets gXPCOMShuttingDown + */ +void Shutdown(); + +} // namespace services +} // namespace mozilla + +#endif diff --git a/xpcom/build/nsXPCOMStrings.cpp b/xpcom/build/nsXPCOMStrings.cpp new file mode 100644 index 000000000..617edbab2 --- /dev/null +++ b/xpcom/build/nsXPCOMStrings.cpp @@ -0,0 +1,366 @@ +/* -*- 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 "nsString.h" +#include "nsCharTraits.h" + +#include "nsXPCOMStrings.h" +#include "nsNativeCharsetUtils.h" + +/* ------------------------------------------------------------------------- */ + +XPCOM_API(nsresult) +NS_StringContainerInit(nsStringContainer& aContainer) +{ + NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsString), + "nsStringContainer is not large enough"); + + // use placement new to avoid heap allocating nsString object + new (&aContainer) nsString(); + + return NS_OK; +} + +XPCOM_API(nsresult) +NS_StringContainerInit2(nsStringContainer& aContainer, + const char16_t* aData, + uint32_t aDataLength, + uint32_t aFlags) +{ + NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsString), + "nsStringContainer is not large enough"); + + if (!aData) { + new (&aContainer) nsString(); + } else { + if (aDataLength == UINT32_MAX) { + if (NS_WARN_IF(aFlags & NS_STRING_CONTAINER_INIT_SUBSTRING)) { + return NS_ERROR_INVALID_ARG; + } + aDataLength = nsCharTraits<char16_t>::length(aData); + } + + if (aFlags & (NS_STRING_CONTAINER_INIT_DEPEND | + NS_STRING_CONTAINER_INIT_ADOPT)) { + uint32_t flags; + if (aFlags & NS_STRING_CONTAINER_INIT_SUBSTRING) { + flags = nsSubstring::F_NONE; + } else { + flags = nsSubstring::F_TERMINATED; + } + + if (aFlags & NS_STRING_CONTAINER_INIT_ADOPT) { + flags |= nsSubstring::F_OWNED; + } + + new (&aContainer) nsSubstring(const_cast<char16_t*>(aData), + aDataLength, flags); + } else { + new (&aContainer) nsString(aData, aDataLength); + } + } + + return NS_OK; +} + +XPCOM_API(void) +NS_StringContainerFinish(nsStringContainer& aContainer) +{ + // call the nsString dtor + reinterpret_cast<nsString*>(&aContainer)->~nsString(); +} + +/* ------------------------------------------------------------------------- */ + +XPCOM_API(uint32_t) +NS_StringGetData(const nsAString& aStr, const char16_t** aData, + bool* aTerminated) +{ + if (aTerminated) { + *aTerminated = aStr.IsTerminated(); + } + + *aData = aStr.BeginReading(); + return aStr.Length(); +} + +XPCOM_API(uint32_t) +NS_StringGetMutableData(nsAString& aStr, uint32_t aDataLength, + char16_t** aData) +{ + if (aDataLength != UINT32_MAX) { + aStr.SetLength(aDataLength); + if (aStr.Length() != aDataLength) { + *aData = nullptr; + return 0; + } + } + + *aData = aStr.BeginWriting(); + return aStr.Length(); +} + +XPCOM_API(char16_t*) +NS_StringCloneData(const nsAString& aStr) +{ + return ToNewUnicode(aStr); +} + +XPCOM_API(nsresult) +NS_StringSetData(nsAString& aStr, const char16_t* aData, uint32_t aDataLength) +{ + aStr.Assign(aData, aDataLength); + return NS_OK; // XXX report errors +} + +XPCOM_API(nsresult) +NS_StringSetDataRange(nsAString& aStr, + uint32_t aCutOffset, uint32_t aCutLength, + const char16_t* aData, uint32_t aDataLength) +{ + if (aCutOffset == UINT32_MAX) { + // append case + if (aData) { + aStr.Append(aData, aDataLength); + } + return NS_OK; // XXX report errors + } + + if (aCutLength == UINT32_MAX) { + aCutLength = aStr.Length() - aCutOffset; + } + + if (aData) { + if (aDataLength == UINT32_MAX) { + aStr.Replace(aCutOffset, aCutLength, nsDependentString(aData)); + } else { + aStr.Replace(aCutOffset, aCutLength, Substring(aData, aDataLength)); + } + } else { + aStr.Cut(aCutOffset, aCutLength); + } + + return NS_OK; // XXX report errors +} + +XPCOM_API(nsresult) +NS_StringCopy(nsAString& aDest, const nsAString& aSrc) +{ + aDest.Assign(aSrc); + return NS_OK; // XXX report errors +} + +XPCOM_API(void) +NS_StringSetIsVoid(nsAString& aStr, const bool aIsVoid) +{ + aStr.SetIsVoid(aIsVoid); +} + +XPCOM_API(bool) +NS_StringGetIsVoid(const nsAString& aStr) +{ + return aStr.IsVoid(); +} + +/* ------------------------------------------------------------------------- */ + +XPCOM_API(nsresult) +NS_CStringContainerInit(nsCStringContainer& aContainer) +{ + NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsCString), + "nsCStringContainer is not large enough"); + + // use placement new to avoid heap allocating nsCString object + new (&aContainer) nsCString(); + + return NS_OK; +} + +XPCOM_API(nsresult) +NS_CStringContainerInit2(nsCStringContainer& aContainer, + const char* aData, + uint32_t aDataLength, + uint32_t aFlags) +{ + NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsCString), + "nsStringContainer is not large enough"); + + if (!aData) { + new (&aContainer) nsCString(); + } else { + if (aDataLength == UINT32_MAX) { + if (NS_WARN_IF(aFlags & NS_CSTRING_CONTAINER_INIT_SUBSTRING)) { + return NS_ERROR_INVALID_ARG; + } + aDataLength = nsCharTraits<char>::length(aData); + } + + if (aFlags & (NS_CSTRING_CONTAINER_INIT_DEPEND | + NS_CSTRING_CONTAINER_INIT_ADOPT)) { + uint32_t flags; + if (aFlags & NS_CSTRING_CONTAINER_INIT_SUBSTRING) { + flags = nsCSubstring::F_NONE; + } else { + flags = nsCSubstring::F_TERMINATED; + } + + if (aFlags & NS_CSTRING_CONTAINER_INIT_ADOPT) { + flags |= nsCSubstring::F_OWNED; + } + + new (&aContainer) nsCSubstring(const_cast<char*>(aData), + aDataLength, flags); + } else { + new (&aContainer) nsCString(aData, aDataLength); + } + } + + return NS_OK; +} + +XPCOM_API(void) +NS_CStringContainerFinish(nsCStringContainer& aContainer) +{ + // call the nsCString dtor + reinterpret_cast<nsCString*>(&aContainer)->~nsCString(); +} + +/* ------------------------------------------------------------------------- */ + +XPCOM_API(uint32_t) +NS_CStringGetData(const nsACString& aStr, const char** aData, + bool* aTerminated) +{ + if (aTerminated) { + *aTerminated = aStr.IsTerminated(); + } + + *aData = aStr.BeginReading(); + return aStr.Length(); +} + +XPCOM_API(uint32_t) +NS_CStringGetMutableData(nsACString& aStr, uint32_t aDataLength, char** aData) +{ + if (aDataLength != UINT32_MAX) { + aStr.SetLength(aDataLength); + if (aStr.Length() != aDataLength) { + *aData = nullptr; + return 0; + } + } + + *aData = aStr.BeginWriting(); + return aStr.Length(); +} + +XPCOM_API(char*) +NS_CStringCloneData(const nsACString& aStr) +{ + return ToNewCString(aStr); +} + +XPCOM_API(nsresult) +NS_CStringSetData(nsACString& aStr, const char* aData, uint32_t aDataLength) +{ + aStr.Assign(aData, aDataLength); + return NS_OK; // XXX report errors +} + +XPCOM_API(nsresult) +NS_CStringSetDataRange(nsACString& aStr, + uint32_t aCutOffset, uint32_t aCutLength, + const char* aData, uint32_t aDataLength) +{ + if (aCutOffset == UINT32_MAX) { + // append case + if (aData) { + aStr.Append(aData, aDataLength); + } + return NS_OK; // XXX report errors + } + + if (aCutLength == UINT32_MAX) { + aCutLength = aStr.Length() - aCutOffset; + } + + if (aData) { + if (aDataLength == UINT32_MAX) { + aStr.Replace(aCutOffset, aCutLength, nsDependentCString(aData)); + } else { + aStr.Replace(aCutOffset, aCutLength, Substring(aData, aDataLength)); + } + } else { + aStr.Cut(aCutOffset, aCutLength); + } + + return NS_OK; // XXX report errors +} + +XPCOM_API(nsresult) +NS_CStringCopy(nsACString& aDest, const nsACString& aSrc) +{ + aDest.Assign(aSrc); + return NS_OK; // XXX report errors +} + +XPCOM_API(void) +NS_CStringSetIsVoid(nsACString& aStr, const bool aIsVoid) +{ + aStr.SetIsVoid(aIsVoid); +} + +XPCOM_API(bool) +NS_CStringGetIsVoid(const nsACString& aStr) +{ + return aStr.IsVoid(); +} + +/* ------------------------------------------------------------------------- */ + +XPCOM_API(nsresult) +NS_CStringToUTF16(const nsACString& aSrc, + nsCStringEncoding aSrcEncoding, + nsAString& aDest) +{ + switch (aSrcEncoding) { + case NS_CSTRING_ENCODING_ASCII: + CopyASCIItoUTF16(aSrc, aDest); + break; + case NS_CSTRING_ENCODING_UTF8: + CopyUTF8toUTF16(aSrc, aDest); + break; + case NS_CSTRING_ENCODING_NATIVE_FILESYSTEM: + NS_CopyNativeToUnicode(aSrc, aDest); + break; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } + + return NS_OK; // XXX report errors +} + +XPCOM_API(nsresult) +NS_UTF16ToCString(const nsAString& aSrc, + nsCStringEncoding aDestEncoding, + nsACString& aDest) +{ + switch (aDestEncoding) { + case NS_CSTRING_ENCODING_ASCII: + LossyCopyUTF16toASCII(aSrc, aDest); + break; + case NS_CSTRING_ENCODING_UTF8: + CopyUTF16toUTF8(aSrc, aDest); + break; + case NS_CSTRING_ENCODING_NATIVE_FILESYSTEM: + NS_CopyUnicodeToNative(aSrc, aDest); + break; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } + + return NS_OK; // XXX report errors +} diff --git a/xpcom/build/nsXREAppData.h b/xpcom/build/nsXREAppData.h new file mode 100644 index 000000000..fbc7adb8f --- /dev/null +++ b/xpcom/build/nsXREAppData.h @@ -0,0 +1,164 @@ +/* -*- 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/. */ + +#ifndef nsXREAppData_h +#define nsXREAppData_h + +#include <stdint.h> +#include "mozilla/Attributes.h" + +class nsIFile; + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +namespace sandbox { +class BrokerServices; +} +#endif + +/** + * Application-specific data needed to start the apprunner. + * + * @note When this structure is allocated and manipulated by XRE_CreateAppData, + * string fields will be allocated with moz_xmalloc, and interface pointers + * are strong references. + */ +struct nsXREAppData +{ + /** + * This should be set to sizeof(nsXREAppData). This structure may be + * extended in future releases, and this ensures that binary compatibility + * is maintained. + */ + uint32_t size; + + /** + * The directory of the application to be run. May be null if the + * xulrunner and the app are installed into the same directory. + */ + nsIFile* MOZ_NON_OWNING_REF directory; + + /** + * The name of the application vendor. This must be ASCII, and is normally + * mixed-case, e.g. "Mozilla". Optional (may be null), but highly + * recommended. Must not be the empty string. + */ + const char* vendor; + + /** + * The name of the application. This must be ASCII, and is normally + * mixed-case, e.g. "Firefox". Required (must not be null or an empty + * string). + */ + const char* name; + + /** + * The internal name of the application for remoting purposes. When left + * unspecified, "name" is used instead. This must be ASCII, and is normally + * lowercase, e.g. "firefox". Optional (may be null but not an empty string). + */ + const char* remotingName; + + /** + * The major version, e.g. "0.8.0+". Optional (may be null), but + * required for advanced application features such as the extension + * manager and update service. Must not be the empty string. + */ + const char* version; + + /** + * The application's build identifier, e.g. "2004051604" + */ + const char* buildID; + + /** + * The application's UUID. Used by the extension manager to determine + * compatible extensions. Optional, but required for advanced application + * features such as the extension manager and update service. + * + * This has traditionally been in the form + * "{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}" but for new applications + * a more readable form is encouraged: "appname@vendor.tld". Only + * the following characters are allowed: a-z A-Z 0-9 - . @ _ { } * + */ + const char* ID; + + /** + * The copyright information to print for the -h commandline flag, + * e.g. "Copyright (c) 2003 mozilla.org". + */ + const char* copyright; + + /** + * Combination of NS_XRE_ prefixed flags (defined below). + */ + uint32_t flags; + + /** + * The location of the XRE. XRE_main may not be able to figure this out + * programatically. + */ + nsIFile* MOZ_NON_OWNING_REF xreDirectory; + + /** + * The minimum/maximum compatible XRE version. + */ + const char* minVersion; + const char* maxVersion; + + /** + * The server URL to send crash reports to. + */ + const char* crashReporterURL; + + /** + * The profile directory that will be used. Optional (may be null). Must not + * be the empty string, must be ASCII. The path is split into components + * along the path separator characters '/' and '\'. + * + * The application data directory ("UAppData", see below) is normally + * composed as follows, where $HOME is platform-specific: + * + * UAppData = $HOME[/$vendor]/$name + * + * If present, the 'profile' string will be used instead of the combination of + * vendor and name as follows: + * + * UAppData = $HOME/$profile + */ + const char* profile; + + /** + * The application name to use in the User Agent string. + */ + const char* UAName; + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + /** + * Chromium sandbox BrokerServices. + */ + sandbox::BrokerServices* sandboxBrokerServices; +#endif +}; + +/** + * Indicates whether or not the profile migrator service may be + * invoked at startup when creating a profile. + */ +#define NS_XRE_ENABLE_PROFILE_MIGRATOR (1 << 1) + +/** + * Indicates the Windows DLL Blocklist initialized properly. For testing + * purposes only. Set in nsBrowserApp on startup, automated tests then + * check the result. + */ +#define NS_XRE_DLL_BLOCKLIST_ENABLED (1 << 2) + +/** + * Indicates whether or not to use Breakpad crash reporting. + */ +#define NS_XRE_ENABLE_CRASH_REPORTER (1 << 3) + +#endif // nsXREAppData_h diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h new file mode 100644 index 000000000..426a58f06 --- /dev/null +++ b/xpcom/build/nsXULAppAPI.h @@ -0,0 +1,538 @@ +/* -*- 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/. */ + +#ifndef _nsXULAppAPI_h__ +#define _nsXULAppAPI_h__ + +#include "nsID.h" +#include "xrecore.h" +#include "nsXPCOM.h" +#include "nsISupports.h" +#include "mozilla/Logging.h" +#include "nsXREAppData.h" +#include "js/TypeDecls.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/Vector.h" +#include "mozilla/TimeStamp.h" +#include "XREChildData.h" +#include "XREShellData.h" + +/** + * A directory service key which provides the platform-correct "application + * data" directory as follows, where $name and $vendor are as defined above and + * $vendor is optional: + * + * Windows: + * HOME = Documents and Settings\$USER\Application Data + * UAppData = $HOME[\$vendor]\$name + * + * Unix: + * HOME = ~ + * UAppData = $HOME/.[$vendor/]$name + * + * Mac: + * HOME = ~ + * UAppData = $HOME/Library/Application Support/$name + * + * Note that the "profile" member above will change the value of UAppData as + * follows: + * + * Windows: + * UAppData = $HOME\$profile + * + * Unix: + * UAppData = $HOME/.$profile + * + * Mac: + * UAppData = $HOME/Library/Application Support/$profile + */ +#define XRE_USER_APP_DATA_DIR "UAppData" + +/** + * A directory service key which provides a list of all enabled extension + * directories and files (packed XPIs). The list includes compatible + * platform-specific extension subdirectories. + * + * @note The directory list will have no members when the application is + * launched in safe mode. + */ +#define XRE_EXTENSIONS_DIR_LIST "XREExtDL" + +/** + * A directory service key which provides the executable file used to + * launch the current process. This is the same value returned by the + * XRE_GetBinaryPath function defined below. + */ +#define XRE_EXECUTABLE_FILE "XREExeF" + +/** + * A directory service key which specifies the profile + * directory. Unlike the NS_APP_USER_PROFILE_50_DIR key, this key may + * be available when the profile hasn't been "started", or after is + * has been shut down. If the application is running without a + * profile, such as when showing the profile manager UI, this key will + * not be available. This key is provided by the XUL apprunner or by + * the aAppDirProvider object passed to XRE_InitEmbedding. + */ +#define NS_APP_PROFILE_DIR_STARTUP "ProfDS" + +/** + * A directory service key which specifies the profile + * directory. Unlike the NS_APP_USER_PROFILE_LOCAL_50_DIR key, this key may + * be available when the profile hasn't been "started", or after is + * has been shut down. If the application is running without a + * profile, such as when showing the profile manager UI, this key will + * not be available. This key is provided by the XUL apprunner or by + * the aAppDirProvider object passed to XRE_InitEmbedding. + */ +#define NS_APP_PROFILE_LOCAL_DIR_STARTUP "ProfLDS" + +/** + * A directory service key which specifies the system extension + * parent directory containing platform-specific extensions. + * This key may not be available on all platforms. + */ +#define XRE_SYS_LOCAL_EXTENSION_PARENT_DIR "XRESysLExtPD" + +/** + * A directory service key which specifies the system extension + * parent directory containing platform-independent extensions. + * This key may not be available on all platforms. + * Additionally, the directory may be equal to that returned by + * XRE_SYS_LOCAL_EXTENSION_PARENT_DIR on some platforms. + */ +#define XRE_SYS_SHARE_EXTENSION_PARENT_DIR "XRESysSExtPD" + +#if defined(XP_UNIX) || defined(XP_MACOSX) +/** + * Directory service keys for the system-wide and user-specific + * directories where host manifests used by the WebExtensions + * native messaging feature are found. + */ +#define XRE_SYS_NATIVE_MESSAGING_MANIFESTS "XRESysNativeMessaging" +#define XRE_USER_NATIVE_MESSAGING_MANIFESTS "XREUserNativeMessaging" +#endif + +/** + * A directory service key which specifies the user system extension + * parent directory. + */ +#define XRE_USER_SYS_EXTENSION_DIR "XREUSysExt" + +/** + * A directory service key which specifies the distribution specific files for + * the application. + */ +#define XRE_APP_DISTRIBUTION_DIR "XREAppDist" + +/** + * A directory service key which specifies the location for system add-ons. + */ +#define XRE_APP_FEATURES_DIR "XREAppFeat" + +/** + * A directory service key which specifies the location for app dir add-ons. + * Should be a synonym for XCurProcD everywhere except in tests. + */ +#define XRE_ADDON_APP_DIR "XREAddonAppDir" + +/** + * A directory service key which provides the update directory. Callers should + * fall back to appDir. + * Windows: If vendor name exists: + * Documents and Settings\<User>\Local Settings\Application Data\ + * <vendor name>\updates\ + * <hash of the path to XRE_EXECUTABLE_FILE’s parent directory> + * + * If vendor name doesn't exist, but product name exists: + * Documents and Settings\<User>\Local Settings\Application Data\ + * <product name>\updates\ + * <hash of the path to XRE_EXECUTABLE_FILE’s parent directory> + * + * If neither vendor nor product name exists: + * If app dir is under Program Files: + * Documents and Settings\<User>\Local Settings\Application Data\ + * <relative path to app dir from Program Files> + * + * If app dir isn’t under Program Files: + * Documents and Settings\<User>\Local Settings\Application Data\ + * <MOZ_APP_NAME> + * + * Mac: ~/Library/Caches/Mozilla/updates/<absolute path to app dir> + * + * Gonk: /data/local + * + * All others: Parent directory of XRE_EXECUTABLE_FILE. + */ +#define XRE_UPDATE_ROOT_DIR "UpdRootD" + +/** + * A directory service key which provides an alternate location + * to UpdRootD to to store large files. This key is currently + * only implemented in the Gonk directory service provider. + */ + +#define XRE_UPDATE_ARCHIVE_DIR "UpdArchD" + +/** + * A directory service key which provides the directory where an OS update is +* applied. + * At present this is supported only in Gonk. + */ +#define XRE_OS_UPDATE_APPLY_TO_DIR "OSUpdApplyToD" + +/** + * Begin an XUL application. Does not return until the user exits the + * application. + * + * @param argc/argv Command-line parameters to pass to the application. On + * Windows, these should be in UTF8. On unix-like platforms + * these are in the "native" character set. + * + * @param aAppData Information about the application to be run. + * + * @param aFlags Platform specific flags. + * + * @return A native result code suitable for returning from main(). + * + * @note If the binary is linked against the standalone XPCOM glue, + * XPCOMGlueStartup() should be called before this method. + */ +XRE_API(int, + XRE_main, (int argc, char* argv[], const nsXREAppData* aAppData, + uint32_t aFlags)) + +/** + * Given a path relative to the current working directory (or an absolute + * path), return an appropriate nsIFile object. + * + * @note Pass UTF8 strings on Windows... native charset on other platforms. + */ +XRE_API(nsresult, + XRE_GetFileFromPath, (const char* aPath, nsIFile** aResult)) + +/** + * Get the path of the running application binary and store it in aResult. + * @param aArgv0 The value passed as argv[0] of main(). This value is only + * used on *nix, and only when other methods of determining + * the binary path have failed. + */ +XRE_API(nsresult, + XRE_GetBinaryPath, (const char* aArgv0, nsIFile** aResult)) + +/** + * Get the static module built in to libxul. + */ +XRE_API(const mozilla::Module*, + XRE_GetStaticModule, ()) + +/** + * Lock a profile directory using platform-specific semantics. + * + * @param aDirectory The profile directory to lock. + * @param aLockObject An opaque lock object. The directory will remain locked + * as long as the XPCOM reference is held. + */ +XRE_API(nsresult, + XRE_LockProfileDirectory, (nsIFile* aDirectory, + nsISupports** aLockObject)) + +/** + * Initialize libXUL for embedding purposes. + * + * @param aLibXULDirectory The directory in which the libXUL shared library + * was found. + * @param aAppDirectory The directory in which the application components + * and resources can be found. This will map to + * the NS_OS_CURRENT_PROCESS_DIR directory service + * key. + * @param aAppDirProvider A directory provider for the application. This + * provider will be aggregated by a libxul provider + * which will provide the base required GRE keys. + * + * @note This function must be called from the "main" thread. + * + * @note At the present time, this function may only be called once in + * a given process. Use XRE_TermEmbedding to clean up and free + * resources allocated by XRE_InitEmbedding. + */ + +XRE_API(nsresult, + XRE_InitEmbedding2, (nsIFile* aLibXULDirectory, + nsIFile* aAppDirectory, + nsIDirectoryServiceProvider* aAppDirProvider)) + +/** + * Register static XPCOM component information. + * This method may be called at any time before or after XRE_main or + * XRE_InitEmbedding. + */ +XRE_API(nsresult, + XRE_AddStaticComponent, (const mozilla::Module* aComponent)) + +/** + * Register XPCOM components found in an array of files/directories. + * This method may be called at any time before or after XRE_main or + * XRE_InitEmbedding. + * + * @param aFiles An array of files or directories. + * @param aFileCount the number of items in the aFiles array. + * @note appdir/components is registered automatically. + * + * NS_APP_LOCATION specifies a location to search for binary XPCOM + * components as well as component/chrome manifest files. + * + * NS_EXTENSION_LOCATION excludes binary XPCOM components but allows other + * manifest instructions. + * + * NS_SKIN_LOCATION specifies a location to search for chrome manifest files + * which are only allowed to register only skin packages and style overlays. + */ +enum NSLocationType +{ + NS_APP_LOCATION, + NS_EXTENSION_LOCATION, + NS_SKIN_LOCATION, + NS_BOOTSTRAPPED_LOCATION +}; + +XRE_API(nsresult, + XRE_AddManifestLocation, (NSLocationType aType, + nsIFile* aLocation)) + +/** + * Register XPCOM components found in a JAR. + * This is similar to XRE_AddManifestLocation except the file specified + * must be a zip archive with a manifest named chrome.manifest + * This method may be called at any time before or after XRE_main or + * XRE_InitEmbedding. + * + * @param aFiles An array of files or directories. + * @param aFileCount the number of items in the aFiles array. + * @note appdir/components is registered automatically. + * + * NS_COMPONENT_LOCATION specifies a location to search for binary XPCOM + * components as well as component/chrome manifest files. + * + * NS_SKIN_LOCATION specifies a location to search for chrome manifest files + * which are only allowed to register only skin packages and style overlays. + */ +XRE_API(nsresult, + XRE_AddJarManifestLocation, (NSLocationType aType, + nsIFile* aLocation)) + +/** + * Fire notifications to inform the toolkit about a new profile. This + * method should be called after XRE_InitEmbedding if the embedder + * wishes to run with a profile. Normally the embedder should call + * XRE_LockProfileDirectory to lock the directory before calling this + * method. + * + * @note There are two possibilities for selecting a profile: + * + * 1) Select the profile before calling XRE_InitEmbedding. The aAppDirProvider + * object passed to XRE_InitEmbedding should provide the + * NS_APP_USER_PROFILE_50_DIR key, and may also provide the following keys: + * - NS_APP_USER_PROFILE_LOCAL_50_DIR + * - NS_APP_PROFILE_DIR_STARTUP + * - NS_APP_PROFILE_LOCAL_DIR_STARTUP + * In this scenario XRE_NotifyProfile should be called immediately after + * XRE_InitEmbedding. Component registration information will be stored in + * the profile and JS components may be stored in the fastload cache. + * + * 2) Select a profile some time after calling XRE_InitEmbedding. In this case + * the embedder must install a directory service provider which provides + * NS_APP_USER_PROFILE_50_DIR and optionally + * NS_APP_USER_PROFILE_LOCAL_50_DIR. Component registration information + * will be stored in the application directory and JS components will not + * fastload. + */ +XRE_API(void, + XRE_NotifyProfile, ()) + +/** + * Terminate embedding started with XRE_InitEmbedding or XRE_InitEmbedding2 + */ +XRE_API(void, + XRE_TermEmbedding, ()) + +/** + * Create a new nsXREAppData structure from an application.ini file. + * + * @param aINIFile The application.ini file to parse. + * @param aAppData A newly-allocated nsXREAppData structure. The caller is + * responsible for freeing this structure using + * XRE_FreeAppData. + */ +XRE_API(nsresult, + XRE_CreateAppData, (nsIFile* aINIFile, + nsXREAppData** aAppData)) + +/** + * Parse an INI file (application.ini or override.ini) into an existing + * nsXREAppData structure. + * + * @param aINIFile The INI file to parse + * @param aAppData The nsXREAppData structure to fill. + */ +XRE_API(nsresult, + XRE_ParseAppData, (nsIFile* aINIFile, + nsXREAppData* aAppData)) + +/** + * Free a nsXREAppData structure that was allocated with XRE_CreateAppData. + */ +XRE_API(void, + XRE_FreeAppData, (nsXREAppData* aAppData)) + +enum GeckoProcessType +{ + GeckoProcessType_Default = 0, + + GeckoProcessType_Plugin, + GeckoProcessType_Content, + + GeckoProcessType_IPDLUnitTest, + + GeckoProcessType_GMPlugin, // Gecko Media Plugin + + GeckoProcessType_GPU, // GPU and compositor process + + GeckoProcessType_End, + GeckoProcessType_Invalid = GeckoProcessType_End +}; + +static const char* const kGeckoProcessTypeString[] = { + "default", + "plugin", + "tab", + "ipdlunittest", + "geckomediaplugin", + "gpu" +}; + +static_assert(MOZ_ARRAY_LENGTH(kGeckoProcessTypeString) == + GeckoProcessType_End, + "Array length mismatch"); + +XRE_API(const char*, + XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType)) + +XRE_API(void, + XRE_SetProcessType, (const char* aProcessTypeString)) + +#if defined(MOZ_CRASHREPORTER) +// Used in the "master" parent process hosting the crash server +XRE_API(bool, + XRE_TakeMinidumpForChild, (uint32_t aChildPid, nsIFile** aDump, + uint32_t* aSequence)) + +// Used in child processes. +XRE_API(bool, + XRE_SetRemoteExceptionHandler, (const char* aPipe)) +#endif + +namespace mozilla { +namespace gmp { +class GMPLoader; +} // namespace gmp +} // namespace mozilla + +XRE_API(nsresult, + XRE_InitChildProcess, (int aArgc, + char* aArgv[], + const XREChildData* aChildData)) + +XRE_API(GeckoProcessType, + XRE_GetProcessType, ()) + +XRE_API(bool, + XRE_IsParentProcess, ()) + +XRE_API(bool, + XRE_IsContentProcess, ()) + +XRE_API(bool, + XRE_IsGPUProcess, ()) + +typedef void (*MainFunction)(void* aData); + +XRE_API(nsresult, + XRE_InitParentProcess, (int aArgc, + char* aArgv[], + MainFunction aMainFunction, + void* aMainFunctionExtraData)) + +XRE_API(int, + XRE_RunIPDLTest, (int aArgc, + char* aArgv[])) + +XRE_API(nsresult, + XRE_RunAppShell, ()) + +XRE_API(nsresult, + XRE_InitCommandLine, (int aArgc, char* aArgv[])) + +XRE_API(nsresult, + XRE_DeinitCommandLine, ()) + +class MessageLoop; + +XRE_API(void, + XRE_ShutdownChildProcess, ()) + +XRE_API(MessageLoop*, + XRE_GetIOMessageLoop, ()) + +XRE_API(bool, + XRE_SendTestShellCommand, (JSContext* aCx, + JSString* aCommand, + void* aCallback)) +XRE_API(bool, + XRE_ShutdownTestShell, ()) + +XRE_API(void, + XRE_InstallX11ErrorHandler, ()) + +XRE_API(void, + XRE_TelemetryAccumulate, (int aID, uint32_t aSample)) + +XRE_API(void, + XRE_StartupTimelineRecord, (int aEvent, mozilla::TimeStamp aWhen)) + +XRE_API(void, + XRE_InitOmnijar, (nsIFile* aGreOmni, + nsIFile* aAppOmni)) +XRE_API(void, + XRE_StopLateWriteChecks, (void)) + +XRE_API(void, + XRE_EnableSameExecutableForContentProc, ()) + +XRE_API(int, + XRE_XPCShellMain, (int argc, char** argv, char** envp, + const XREShellData* aShellData)) + +#if MOZ_WIDGET_GTK == 2 +XRE_API(void, + XRE_GlibInit, ()) +#endif + + +#ifdef LIBFUZZER +#include "LibFuzzerRegistry.h" + +XRE_API(void, + XRE_LibFuzzerSetMain, (int, char**, LibFuzzerMain)) + +XRE_API(void, + XRE_LibFuzzerGetFuncs, (const char*, LibFuzzerInitFunc*, + LibFuzzerTestingFunc*)) +#endif // LIBFUZZER + +#endif // _nsXULAppAPI_h__ diff --git a/xpcom/build/perfprobe.cpp b/xpcom/build/perfprobe.cpp new file mode 100644 index 000000000..118e73fc8 --- /dev/null +++ b/xpcom/build/perfprobe.cpp @@ -0,0 +1,242 @@ +/* -*- 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/. */ + +/***************************** + Windows implementation of probes, using xperf + *****************************/ +#include <windows.h> +#include <wmistr.h> +#include <evntrace.h> + +#include "perfprobe.h" +#include "nsAutoPtr.h" + +namespace mozilla { +namespace probes { + +#if defined(MOZ_LOGGING) +static LazyLogModule sProbeLog("SysProbe"); +#define LOG(x) MOZ_LOG(sProbeLog, mozilla::LogLevel::Debug, x) +#else +#define LOG(x) +#endif + +// Utility function +GUID +CID_to_GUID(const nsCID& aCID) +{ + GUID result; + result.Data1 = aCID.m0; + result.Data2 = aCID.m1; + result.Data3 = aCID.m2; + for (int i = 0; i < 8; ++i) { + result.Data4[i] = aCID.m3[i]; + } + return result; +} + + +// Implementation of Probe + +Probe::Probe(const nsCID& aGUID, + const nsACString& aName, + ProbeManager* aManager) + : mGUID(CID_to_GUID(aGUID)) + , mName(aName) + , mManager(aManager) +{ +} + +nsresult +Probe::Trigger() +{ + if (!(mManager->mIsActive)) { + //Do not trigger if there is no session + return NS_OK; + } + + _EVENT_TRACE_HEADER event; + ZeroMemory(&event, sizeof(event)); + event.Size = sizeof(event); + event.Flags = WNODE_FLAG_TRACED_GUID ; + event.Guid = (const GUID)mGUID; + event.Class.Type = 1; + event.Class.Version = 0; + event.Class.Level = TRACE_LEVEL_INFORMATION; + + ULONG result = TraceEvent(mManager->mSessionHandle, &event); + + LOG(("Probes: Triggered %s, %s, %ld", + mName.Data(), + result == ERROR_SUCCESS ? "success" : "failure", + result)); + + nsresult rv; + switch (result) { + case ERROR_SUCCESS: + rv = NS_OK; + break; + case ERROR_INVALID_FLAG_NUMBER: + case ERROR_MORE_DATA: + case ERROR_INVALID_PARAMETER: + rv = NS_ERROR_INVALID_ARG; + break; + case ERROR_INVALID_HANDLE: + rv = NS_ERROR_FAILURE; + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + rv = NS_ERROR_OUT_OF_MEMORY; + break; + default: + rv = NS_ERROR_UNEXPECTED; + } + return rv; +} + + +// Implementation of ProbeManager + +ProbeManager::~ProbeManager() +{ + //If the manager goes out of scope, stop the session. + if (mIsActive && mRegistrationHandle) { + StopSession(); + } +} + +ProbeManager::ProbeManager(const nsCID& aApplicationUID, + const nsACString& aApplicationName) + : mIsActive(false) + , mApplicationUID(aApplicationUID) + , mApplicationName(aApplicationName) + , mSessionHandle(0) + , mRegistrationHandle(0) + , mInitialized(false) +{ +#if defined(MOZ_LOGGING) + char cidStr[NSID_LENGTH]; + aApplicationUID.ToProvidedString(cidStr); + LOG(("ProbeManager::Init for application %s, %s", + aApplicationName.Data(), cidStr)); +#endif +} + +//Note: The Windows API is just a little bit scary there. +//The only way to obtain the session handle is to +//- ignore the session handle obtained from RegisterTraceGuids +//- pass a callback +//- in that callback, request the session handle through +// GetTraceLoggerHandle and some opaque value received by the callback + +ULONG WINAPI +ControlCallback(WMIDPREQUESTCODE aRequestCode, + PVOID aContext, + ULONG* aReserved, + PVOID aBuffer) +{ + ProbeManager* context = (ProbeManager*)aContext; + switch (aRequestCode) { + case WMI_ENABLE_EVENTS: { + context->mIsActive = true; + TRACEHANDLE sessionHandle = GetTraceLoggerHandle(aBuffer); + //Note: We only accept one handle + if ((HANDLE)sessionHandle == INVALID_HANDLE_VALUE) { + ULONG result = GetLastError(); + LOG(("Probes: ControlCallback failed, %ul", result)); + return result; + } else if (context->mIsActive && context->mSessionHandle && + context->mSessionHandle != sessionHandle) { + LOG(("Probes: Can only handle one context at a time, " + "ignoring activation")); + return ERROR_SUCCESS; + } else { + context->mSessionHandle = sessionHandle; + LOG(("Probes: ControlCallback activated")); + return ERROR_SUCCESS; + } + } + + case WMI_DISABLE_EVENTS: + context->mIsActive = false; + context->mSessionHandle = 0; + LOG(("Probes: ControlCallback deactivated")); + return ERROR_SUCCESS; + + default: + LOG(("Probes: ControlCallback does not know what to do with %d", + aRequestCode)); + return ERROR_INVALID_PARAMETER; + } +} + +already_AddRefed<Probe> +ProbeManager::GetProbe(const nsCID& aEventUID, const nsACString& aEventName) +{ + RefPtr<Probe> result(new Probe(aEventUID, aEventName, this)); + mAllProbes.AppendElement(result); + return result.forget(); +} + +nsresult +ProbeManager::StartSession() +{ + return StartSession(mAllProbes); +} + +nsresult +ProbeManager::StartSession(nsTArray<RefPtr<Probe>>& aProbes) +{ + const size_t probesCount = aProbes.Length(); + _TRACE_GUID_REGISTRATION* probes = new _TRACE_GUID_REGISTRATION[probesCount]; + for (unsigned int i = 0; i < probesCount; ++i) { + const Probe* probe = aProbes[i]; + const Probe* probeX = static_cast<const Probe*>(probe); + probes[i].Guid = (LPCGUID)&probeX->mGUID; + } + ULONG result = + RegisterTraceGuids(&ControlCallback + /*RequestAddress: Sets mSessions appropriately.*/, + this + /*RequestContext: Passed to ControlCallback*/, + (LPGUID)&mApplicationUID + /*ControlGuid: Tracing GUID + the cast comes from MSDN examples*/, + probesCount + /*GuidCount: Number of probes*/, + probes + /*TraceGuidReg: Probes registration*/, + nullptr + /*MofImagePath: Must be nullptr, says MSDN*/, + nullptr + /*MofResourceName:Must be nullptr, says MSDN*/, + &mRegistrationHandle + /*RegistrationHandle: Handler. + used only for unregistration*/ + ); + delete[] probes; + if (NS_WARN_IF(result != ERROR_SUCCESS)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +nsresult +ProbeManager::StopSession() +{ + LOG(("Probes: Stopping measures")); + if (mSessionHandle != 0) { + ULONG result = UnregisterTraceGuids(mSessionHandle); + mSessionHandle = 0; + if (result != ERROR_SUCCESS) { + return NS_ERROR_INVALID_ARG; + } + } + return NS_OK; +} + +} // namespace probes +} // namespace mozilla diff --git a/xpcom/build/perfprobe.h b/xpcom/build/perfprobe.h new file mode 100644 index 000000000..bc3563654 --- /dev/null +++ b/xpcom/build/perfprobe.h @@ -0,0 +1,204 @@ +/* -*- 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/. */ + +/** + * A mechanism for interacting with operating system-provided + * debugging/profiling tools such as Microsoft EWT/Windows Performance Toolkit. + */ + +#ifndef mozilla_perfprobe_h +#define mozilla_perfprobe_h + +#if !defined(XP_WIN) +#error "For the moment, perfprobe.h is defined only for Windows platforms" +#endif + +#include "nsError.h" +#include "nsString.h" +#include "mozilla/Logging.h" +#include "nsTArray.h" +#include "nsAutoPtr.h" +#include <windows.h> +#undef GetStartupInfo //Prevent Windows from polluting global namespace +#include <wmistr.h> +#include <evntrace.h> + +namespace mozilla { +namespace probes { + +class ProbeManager; + +/** + * A data structure supporting a trigger operation that can be used to + * send information to the operating system. + */ + +class Probe +{ +public: + NS_INLINE_DECL_REFCOUNTING(Probe) + + /** + * Trigger the event. + * + * Note: Can be called from any thread. + */ + nsresult Trigger(); + +protected: + ~Probe() {}; + + Probe(const nsCID& aGUID, const nsACString& aName, ProbeManager* aManager); + friend class ProbeManager; + +protected: + + /** + * The system GUID associated to this probe. See the documentation + * of |ProbeManager::Make| for more details. + */ + const GUID mGUID; + + /** + * The name of this probe. See the documentation + * of |ProbeManager::Make| for more details. + */ + const nsCString mName; + + /** + * The ProbeManager managing this probe. + * + * Note: This is a weak reference to avoid a useless cycle. + */ + class ProbeManager* mManager; +}; + + +/** + * A manager for a group of probes. + * + * You can have several managers in one application, provided that they all + * have distinct IDs and names. However, having more than 2 is considered a bad + * practice. + */ +class ProbeManager +{ +public: + NS_INLINE_DECL_REFCOUNTING(ProbeManager) + + /** + * Create a new probe manager. + * + * This constructor should be called from the main thread. + * + * @param aApplicationUID The unique ID of the probe. Under Windows, this + * unique ID must have been previously registered using an external tool. + * See MyCategory on http://msdn.microsoft.com/en-us/library/aa364100.aspx + * @param aApplicationName A name for the probe. Currently used only for + * logging purposes. In the future, may be attached to the data sent to the + * operating system. + * + * Note: If two ProbeManagers are constructed with the same uid and/or name, + * behavior is unspecified. + */ + ProbeManager(const nsCID& aApplicationUID, + const nsACString& aApplicationName); + + /** + * Acquire a probe. + * + * Note: Only probes acquired before the call to SetReady are taken into + * account + * Note: Can be called only from the main thread. + * + * @param aEventUID The unique ID of the probe. Under Windows, this unique + * ID must have been previously registered using an external tool. + * See MyCategory on http://msdn.microsoft.com/en-us/library/aa364100.aspx + * @param aEventName A name for the probe. Currently used only for logging + * purposes. In the + * future, may be attached to the data sent to the operating system. + * @return Either |null| in case of error or a valid |Probe*|. + * + * Note: If this method is called twice with the same uid and/or name, + * behavior is undefined. + */ + already_AddRefed<Probe> GetProbe(const nsCID& aEventUID, + const nsACString& aEventName); + + /** + * Start/stop the measuring session. + * + * This method should be called from the main thread. + * + * Note that starting an already started probe manager has no effect, + * nor does stopping an already stopped probe manager. + */ + nsresult StartSession(); + nsresult StopSession(); + + /** + * @return true If measures are currently on, i.e. if triggering probes is any + * is useful. You do not have to check this before triggering a probe, unless + * this can avoid complex computations. + */ + bool IsActive(); + +protected: + ~ProbeManager(); + + nsresult StartSession(nsTArray<RefPtr<Probe>>& aProbes); + nsresult Init(const nsCID& aApplicationUID, + const nsACString& aApplicationName); + +protected: + /** + * `true` if a session is in activity, `false` otherwise. + */ + bool mIsActive; + + /** + * The UID of this manager. + * See documentation above for registration steps that you + * may have to take. + */ + nsCID mApplicationUID; + + /** + * The name of the application. + */ + nsCString mApplicationName; + + /** + * All the probes that have been created for this manager. + */ + nsTArray<RefPtr<Probe>> mAllProbes; + + /** + * Handle used for triggering events + */ + TRACEHANDLE mSessionHandle; + + /** + * Handle used for registration/unregistration + */ + TRACEHANDLE mRegistrationHandle; + + /** + * `true` if initialization has been performed, `false` until then. + */ + bool mInitialized; + + friend class Probe; // Needs to access |mSessionHandle| + friend ULONG WINAPI ControlCallback(WMIDPREQUESTCODE aRequestCode, + PVOID aContext, + ULONG* aReserved, + PVOID aBuffer); // Sets |mSessionHandle| +}; + +} // namespace probes +} // namespace mozilla + +#endif //mozilla_perfprobe_h diff --git a/xpcom/build/xpcom_alpha.def b/xpcom/build/xpcom_alpha.def new file mode 100644 index 000000000..38fedfa17 --- /dev/null +++ b/xpcom/build/xpcom_alpha.def @@ -0,0 +1,256 @@ +;+# 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/. + +LIBRARY xpcom +DESCRIPTION "xpcom library" + +EXPORTS + ?Stub3@nsXPTCStubBase@@UAAIXZ + ?Stub4@nsXPTCStubBase@@UAAIXZ + ?Stub5@nsXPTCStubBase@@UAAIXZ + ?Stub6@nsXPTCStubBase@@UAAIXZ + ?Stub7@nsXPTCStubBase@@UAAIXZ + ?Stub8@nsXPTCStubBase@@UAAIXZ + ?Stub9@nsXPTCStubBase@@UAAIXZ + ?Stub10@nsXPTCStubBase@@UAAIXZ + ?Stub11@nsXPTCStubBase@@UAAIXZ + ?Stub12@nsXPTCStubBase@@UAAIXZ + ?Stub13@nsXPTCStubBase@@UAAIXZ + ?Stub14@nsXPTCStubBase@@UAAIXZ + ?Stub15@nsXPTCStubBase@@UAAIXZ + ?Stub16@nsXPTCStubBase@@UAAIXZ + ?Stub17@nsXPTCStubBase@@UAAIXZ + ?Stub18@nsXPTCStubBase@@UAAIXZ + ?Stub19@nsXPTCStubBase@@UAAIXZ + ?Stub20@nsXPTCStubBase@@UAAIXZ + ?Stub21@nsXPTCStubBase@@UAAIXZ + ?Stub22@nsXPTCStubBase@@UAAIXZ + ?Stub23@nsXPTCStubBase@@UAAIXZ + ?Stub24@nsXPTCStubBase@@UAAIXZ + ?Stub25@nsXPTCStubBase@@UAAIXZ + ?Stub26@nsXPTCStubBase@@UAAIXZ + ?Stub27@nsXPTCStubBase@@UAAIXZ + ?Stub28@nsXPTCStubBase@@UAAIXZ + ?Stub29@nsXPTCStubBase@@UAAIXZ + ?Stub30@nsXPTCStubBase@@UAAIXZ + ?Stub31@nsXPTCStubBase@@UAAIXZ + ?Stub32@nsXPTCStubBase@@UAAIXZ + ?Stub33@nsXPTCStubBase@@UAAIXZ + ?Stub34@nsXPTCStubBase@@UAAIXZ + ?Stub35@nsXPTCStubBase@@UAAIXZ + ?Stub36@nsXPTCStubBase@@UAAIXZ + ?Stub37@nsXPTCStubBase@@UAAIXZ + ?Stub38@nsXPTCStubBase@@UAAIXZ + ?Stub39@nsXPTCStubBase@@UAAIXZ + ?Stub40@nsXPTCStubBase@@UAAIXZ + ?Stub41@nsXPTCStubBase@@UAAIXZ + ?Stub42@nsXPTCStubBase@@UAAIXZ + ?Stub43@nsXPTCStubBase@@UAAIXZ + ?Stub44@nsXPTCStubBase@@UAAIXZ + ?Stub45@nsXPTCStubBase@@UAAIXZ + ?Stub46@nsXPTCStubBase@@UAAIXZ + ?Stub47@nsXPTCStubBase@@UAAIXZ + ?Stub48@nsXPTCStubBase@@UAAIXZ + ?Stub49@nsXPTCStubBase@@UAAIXZ + ?Stub50@nsXPTCStubBase@@UAAIXZ + ?Stub51@nsXPTCStubBase@@UAAIXZ + ?Stub52@nsXPTCStubBase@@UAAIXZ + ?Stub53@nsXPTCStubBase@@UAAIXZ + ?Stub54@nsXPTCStubBase@@UAAIXZ + ?Stub55@nsXPTCStubBase@@UAAIXZ + ?Stub56@nsXPTCStubBase@@UAAIXZ + ?Stub57@nsXPTCStubBase@@UAAIXZ + ?Stub58@nsXPTCStubBase@@UAAIXZ + ?Stub59@nsXPTCStubBase@@UAAIXZ + ?Stub60@nsXPTCStubBase@@UAAIXZ + ?Stub61@nsXPTCStubBase@@UAAIXZ + ?Stub62@nsXPTCStubBase@@UAAIXZ + ?Stub63@nsXPTCStubBase@@UAAIXZ + ?Stub64@nsXPTCStubBase@@UAAIXZ + ?Stub65@nsXPTCStubBase@@UAAIXZ + ?Stub66@nsXPTCStubBase@@UAAIXZ + ?Stub67@nsXPTCStubBase@@UAAIXZ + ?Stub68@nsXPTCStubBase@@UAAIXZ + ?Stub69@nsXPTCStubBase@@UAAIXZ + ?Stub70@nsXPTCStubBase@@UAAIXZ + ?Stub71@nsXPTCStubBase@@UAAIXZ + ?Stub72@nsXPTCStubBase@@UAAIXZ + ?Stub73@nsXPTCStubBase@@UAAIXZ + ?Stub74@nsXPTCStubBase@@UAAIXZ + ?Stub75@nsXPTCStubBase@@UAAIXZ + ?Stub76@nsXPTCStubBase@@UAAIXZ + ?Stub77@nsXPTCStubBase@@UAAIXZ + ?Stub78@nsXPTCStubBase@@UAAIXZ + ?Stub79@nsXPTCStubBase@@UAAIXZ + ?Stub80@nsXPTCStubBase@@UAAIXZ + ?Stub81@nsXPTCStubBase@@UAAIXZ + ?Stub82@nsXPTCStubBase@@UAAIXZ + ?Stub83@nsXPTCStubBase@@UAAIXZ + ?Stub84@nsXPTCStubBase@@UAAIXZ + ?Stub85@nsXPTCStubBase@@UAAIXZ + ?Stub86@nsXPTCStubBase@@UAAIXZ + ?Stub87@nsXPTCStubBase@@UAAIXZ + ?Stub88@nsXPTCStubBase@@UAAIXZ + ?Stub89@nsXPTCStubBase@@UAAIXZ + ?Stub90@nsXPTCStubBase@@UAAIXZ + ?Stub91@nsXPTCStubBase@@UAAIXZ + ?Stub92@nsXPTCStubBase@@UAAIXZ + ?Stub93@nsXPTCStubBase@@UAAIXZ + ?Stub94@nsXPTCStubBase@@UAAIXZ + ?Stub95@nsXPTCStubBase@@UAAIXZ + ?Stub96@nsXPTCStubBase@@UAAIXZ + ?Stub97@nsXPTCStubBase@@UAAIXZ + ?Stub98@nsXPTCStubBase@@UAAIXZ + ?Stub99@nsXPTCStubBase@@UAAIXZ + ?Stub100@nsXPTCStubBase@@UAAIXZ + ?Stub101@nsXPTCStubBase@@UAAIXZ + ?Stub102@nsXPTCStubBase@@UAAIXZ + ?Stub103@nsXPTCStubBase@@UAAIXZ + ?Stub104@nsXPTCStubBase@@UAAIXZ + ?Stub105@nsXPTCStubBase@@UAAIXZ + ?Stub106@nsXPTCStubBase@@UAAIXZ + ?Stub107@nsXPTCStubBase@@UAAIXZ + ?Stub108@nsXPTCStubBase@@UAAIXZ + ?Stub109@nsXPTCStubBase@@UAAIXZ + ?Stub110@nsXPTCStubBase@@UAAIXZ + ?Stub111@nsXPTCStubBase@@UAAIXZ + ?Stub112@nsXPTCStubBase@@UAAIXZ + ?Stub113@nsXPTCStubBase@@UAAIXZ + ?Stub114@nsXPTCStubBase@@UAAIXZ + ?Stub115@nsXPTCStubBase@@UAAIXZ + ?Stub116@nsXPTCStubBase@@UAAIXZ + ?Stub117@nsXPTCStubBase@@UAAIXZ + ?Stub118@nsXPTCStubBase@@UAAIXZ + ?Stub119@nsXPTCStubBase@@UAAIXZ + ?Stub120@nsXPTCStubBase@@UAAIXZ + ?Stub121@nsXPTCStubBase@@UAAIXZ + ?Stub122@nsXPTCStubBase@@UAAIXZ + ?Stub123@nsXPTCStubBase@@UAAIXZ + ?Stub124@nsXPTCStubBase@@UAAIXZ + ?Stub125@nsXPTCStubBase@@UAAIXZ + ?Stub126@nsXPTCStubBase@@UAAIXZ + ?Stub127@nsXPTCStubBase@@UAAIXZ + ?Stub128@nsXPTCStubBase@@UAAIXZ + ?Stub129@nsXPTCStubBase@@UAAIXZ + ?Stub130@nsXPTCStubBase@@UAAIXZ + ?Stub131@nsXPTCStubBase@@UAAIXZ + ?Stub132@nsXPTCStubBase@@UAAIXZ + ?Stub133@nsXPTCStubBase@@UAAIXZ + ?Stub134@nsXPTCStubBase@@UAAIXZ + ?Stub135@nsXPTCStubBase@@UAAIXZ + ?Stub136@nsXPTCStubBase@@UAAIXZ + ?Stub137@nsXPTCStubBase@@UAAIXZ + ?Stub138@nsXPTCStubBase@@UAAIXZ + ?Stub139@nsXPTCStubBase@@UAAIXZ + ?Stub140@nsXPTCStubBase@@UAAIXZ + ?Stub141@nsXPTCStubBase@@UAAIXZ + ?Stub142@nsXPTCStubBase@@UAAIXZ + ?Stub143@nsXPTCStubBase@@UAAIXZ + ?Stub144@nsXPTCStubBase@@UAAIXZ + ?Stub145@nsXPTCStubBase@@UAAIXZ + ?Stub146@nsXPTCStubBase@@UAAIXZ + ?Stub147@nsXPTCStubBase@@UAAIXZ + ?Stub148@nsXPTCStubBase@@UAAIXZ + ?Stub149@nsXPTCStubBase@@UAAIXZ + ?Stub150@nsXPTCStubBase@@UAAIXZ + ?Stub151@nsXPTCStubBase@@UAAIXZ + ?Stub152@nsXPTCStubBase@@UAAIXZ + ?Stub153@nsXPTCStubBase@@UAAIXZ + ?Stub154@nsXPTCStubBase@@UAAIXZ + ?Stub155@nsXPTCStubBase@@UAAIXZ + ?Stub156@nsXPTCStubBase@@UAAIXZ + ?Stub157@nsXPTCStubBase@@UAAIXZ + ?Stub158@nsXPTCStubBase@@UAAIXZ + ?Stub159@nsXPTCStubBase@@UAAIXZ + ?Stub160@nsXPTCStubBase@@UAAIXZ + ?Stub161@nsXPTCStubBase@@UAAIXZ + ?Stub162@nsXPTCStubBase@@UAAIXZ + ?Stub163@nsXPTCStubBase@@UAAIXZ + ?Stub164@nsXPTCStubBase@@UAAIXZ + ?Stub165@nsXPTCStubBase@@UAAIXZ + ?Stub166@nsXPTCStubBase@@UAAIXZ + ?Stub167@nsXPTCStubBase@@UAAIXZ + ?Stub168@nsXPTCStubBase@@UAAIXZ + ?Stub169@nsXPTCStubBase@@UAAIXZ + ?Stub170@nsXPTCStubBase@@UAAIXZ + ?Stub171@nsXPTCStubBase@@UAAIXZ + ?Stub172@nsXPTCStubBase@@UAAIXZ + ?Stub173@nsXPTCStubBase@@UAAIXZ + ?Stub174@nsXPTCStubBase@@UAAIXZ + ?Stub175@nsXPTCStubBase@@UAAIXZ + ?Stub176@nsXPTCStubBase@@UAAIXZ + ?Stub177@nsXPTCStubBase@@UAAIXZ + ?Stub178@nsXPTCStubBase@@UAAIXZ + ?Stub179@nsXPTCStubBase@@UAAIXZ + ?Stub180@nsXPTCStubBase@@UAAIXZ + ?Stub181@nsXPTCStubBase@@UAAIXZ + ?Stub182@nsXPTCStubBase@@UAAIXZ + ?Stub183@nsXPTCStubBase@@UAAIXZ + ?Stub184@nsXPTCStubBase@@UAAIXZ + ?Stub185@nsXPTCStubBase@@UAAIXZ + ?Stub186@nsXPTCStubBase@@UAAIXZ + ?Stub187@nsXPTCStubBase@@UAAIXZ + ?Stub188@nsXPTCStubBase@@UAAIXZ + ?Stub189@nsXPTCStubBase@@UAAIXZ + ?Stub190@nsXPTCStubBase@@UAAIXZ + ?Stub191@nsXPTCStubBase@@UAAIXZ + ?Stub192@nsXPTCStubBase@@UAAIXZ + ?Stub193@nsXPTCStubBase@@UAAIXZ + ?Stub194@nsXPTCStubBase@@UAAIXZ + ?Stub195@nsXPTCStubBase@@UAAIXZ + ?Stub196@nsXPTCStubBase@@UAAIXZ + ?Stub197@nsXPTCStubBase@@UAAIXZ + ?Stub198@nsXPTCStubBase@@UAAIXZ + ?Stub199@nsXPTCStubBase@@UAAIXZ + ?Stub200@nsXPTCStubBase@@UAAIXZ + ?Stub201@nsXPTCStubBase@@UAAIXZ + ?Stub202@nsXPTCStubBase@@UAAIXZ + ?Stub203@nsXPTCStubBase@@UAAIXZ + ?Stub204@nsXPTCStubBase@@UAAIXZ + ?Stub205@nsXPTCStubBase@@UAAIXZ + ?Stub206@nsXPTCStubBase@@UAAIXZ + ?Stub207@nsXPTCStubBase@@UAAIXZ + ?Stub208@nsXPTCStubBase@@UAAIXZ + ?Stub209@nsXPTCStubBase@@UAAIXZ + ?Stub210@nsXPTCStubBase@@UAAIXZ + ?Stub211@nsXPTCStubBase@@UAAIXZ + ?Stub212@nsXPTCStubBase@@UAAIXZ + ?Stub213@nsXPTCStubBase@@UAAIXZ + ?Stub214@nsXPTCStubBase@@UAAIXZ + ?Stub215@nsXPTCStubBase@@UAAIXZ + ?Stub216@nsXPTCStubBase@@UAAIXZ + ?Stub217@nsXPTCStubBase@@UAAIXZ + ?Stub218@nsXPTCStubBase@@UAAIXZ + ?Stub219@nsXPTCStubBase@@UAAIXZ + ?Stub220@nsXPTCStubBase@@UAAIXZ + ?Stub221@nsXPTCStubBase@@UAAIXZ + ?Stub222@nsXPTCStubBase@@UAAIXZ + ?Stub223@nsXPTCStubBase@@UAAIXZ + ?Stub224@nsXPTCStubBase@@UAAIXZ + ?Stub225@nsXPTCStubBase@@UAAIXZ + ?Stub226@nsXPTCStubBase@@UAAIXZ + ?Stub227@nsXPTCStubBase@@UAAIXZ + ?Stub228@nsXPTCStubBase@@UAAIXZ + ?Stub229@nsXPTCStubBase@@UAAIXZ + ?Stub230@nsXPTCStubBase@@UAAIXZ + ?Stub231@nsXPTCStubBase@@UAAIXZ + ?Stub232@nsXPTCStubBase@@UAAIXZ + ?Stub233@nsXPTCStubBase@@UAAIXZ + ?Stub234@nsXPTCStubBase@@UAAIXZ + ?Stub235@nsXPTCStubBase@@UAAIXZ + ?Stub236@nsXPTCStubBase@@UAAIXZ + ?Stub237@nsXPTCStubBase@@UAAIXZ + ?Stub238@nsXPTCStubBase@@UAAIXZ + ?Stub239@nsXPTCStubBase@@UAAIXZ + ?Stub240@nsXPTCStubBase@@UAAIXZ + ?Stub241@nsXPTCStubBase@@UAAIXZ + ?Stub242@nsXPTCStubBase@@UAAIXZ + ?Stub243@nsXPTCStubBase@@UAAIXZ + ?Stub244@nsXPTCStubBase@@UAAIXZ + ?Stub245@nsXPTCStubBase@@UAAIXZ + ?Stub246@nsXPTCStubBase@@UAAIXZ + ?Stub247@nsXPTCStubBase@@UAAIXZ + ?Stub248@nsXPTCStubBase@@UAAIXZ + ?Stub249@nsXPTCStubBase@@UAAIXZ + diff --git a/xpcom/build/xrecore.h b/xpcom/build/xrecore.h new file mode 100644 index 000000000..9749a8e90 --- /dev/null +++ b/xpcom/build/xrecore.h @@ -0,0 +1,25 @@ +/* -*- 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/. */ + +#ifndef xrecore_h__ +#define xrecore_h__ + +#include "nscore.h" + +/** + * Import/export macros for libXUL APIs. + */ +#ifdef XPCOM_GLUE +#define XRE_API(type, name, params) \ + typedef type (NS_FROZENCALL * name##Type) params; \ + extern name##Type name NS_HIDDEN; +#elif defined(IMPL_LIBXUL) +#define XRE_API(type, name, params) EXPORT_XPCOM_API(type) name params; +#else +#define XRE_API(type, name, params) IMPORT_XPCOM_API(type) name params; +#endif + +#endif // xrecore_h__ |