summaryrefslogtreecommitdiffstats
path: root/xpcom/build/NSPRInterposer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/build/NSPRInterposer.cpp')
-rw-r--r--xpcom/build/NSPRInterposer.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/xpcom/build/NSPRInterposer.cpp b/xpcom/build/NSPRInterposer.cpp
new file mode 100644
index 0000000000..184c3793e7
--- /dev/null
+++ b/xpcom/build/NSPRInterposer.cpp
@@ -0,0 +1,207 @@
+/* -*- 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"
+
+#include <sys/param.h>
+#ifdef XP_MACOSX
+# include <fcntl.h>
+#else
+# include "prprf.h"
+# include <unistd.h>
+#endif
+
+namespace {
+
+/* Original IO methods */
+PRCloseFN sCloseFn = nullptr;
+PRReadFN sReadFn = nullptr;
+PRWriteFN sWriteFn = nullptr;
+PRFsyncFN sFSyncFn = nullptr;
+PRFileInfoFN sFileInfoFn = nullptr;
+PRFileInfo64FN sFileInfo64Fn = nullptr;
+
+static int32_t GetPathFromFd(int32_t aFd, char* aBuf, size_t aBufSize) {
+#ifdef XP_MACOSX
+ NS_ASSERTION(aBufSize >= MAXPATHLEN,
+ "aBufSize should be a least MAXPATHLEN long");
+
+ return fcntl(aFd, F_GETPATH, aBuf);
+#else
+ char procPath[32];
+ if (PR_snprintf(procPath, sizeof(procPath), "/proc/self/fd/%i", aFd) ==
+ (PRUint32)-1) {
+ return -1;
+ }
+
+ int32_t ret = readlink(procPath, aBuf, aBufSize - 1);
+ if (ret > -1) {
+ aBuf[ret] = '\0';
+ }
+
+ return ret;
+#endif
+}
+
+/**
+ * RAII class for timing the duration of an NSPR I/O call and reporting the
+ * result to the mozilla::IOInterposeObserver API.
+ */
+class NSPRIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
+ public:
+ explicit NSPRIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
+ PRFileDesc* aFd)
+ : mozilla::IOInterposeObserver::Observation(aOp, "NSPRIOInterposer") {
+ char filename[MAXPATHLEN];
+ if (mShouldReport && aFd &&
+ GetPathFromFd(PR_FileDesc2NativeHandle(aFd), filename,
+ sizeof(filename)) != -1) {
+ CopyUTF8toUTF16(mozilla::MakeStringSpan(filename), mFilename);
+ } else {
+ mFilename.Truncate();
+ }
+ }
+
+ void Filename(nsAString& aFilename) override { aFilename = mFilename; }
+
+ ~NSPRIOAutoObservation() override { Report(); }
+
+ private:
+ nsString mFilename;
+};
+
+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(mozilla::IOInterposeObserver::OpClose, aFd);
+ 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(mozilla::IOInterposeObserver::OpRead, aFd);
+ 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(mozilla::IOInterposeObserver::OpWrite, aFd);
+ 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(mozilla::IOInterposeObserver::OpFSync, aFd);
+ 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(mozilla::IOInterposeObserver::OpStat, aFd);
+ 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(mozilla::IOInterposeObserver::OpStat, aFd);
+ 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