Bug 1910362 - Create new Nimbus helper r=aaronmt,ohorvath
[gecko.git] / xpcom / build / NSPRInterposer.cpp
blob184c3793e746a58f6f6aa1b9b4d761e1b8e26ed5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "IOInterposer.h"
8 #include "NSPRInterposer.h"
10 #include "prio.h"
11 #include "private/pprio.h"
12 #include "nsDebug.h"
13 #include "nscore.h"
15 #include <sys/param.h>
16 #ifdef XP_MACOSX
17 # include <fcntl.h>
18 #else
19 # include "prprf.h"
20 # include <unistd.h>
21 #endif
23 namespace {
25 /* Original IO methods */
26 PRCloseFN sCloseFn = nullptr;
27 PRReadFN sReadFn = nullptr;
28 PRWriteFN sWriteFn = nullptr;
29 PRFsyncFN sFSyncFn = nullptr;
30 PRFileInfoFN sFileInfoFn = nullptr;
31 PRFileInfo64FN sFileInfo64Fn = nullptr;
33 static int32_t GetPathFromFd(int32_t aFd, char* aBuf, size_t aBufSize) {
34 #ifdef XP_MACOSX
35 NS_ASSERTION(aBufSize >= MAXPATHLEN,
36 "aBufSize should be a least MAXPATHLEN long");
38 return fcntl(aFd, F_GETPATH, aBuf);
39 #else
40 char procPath[32];
41 if (PR_snprintf(procPath, sizeof(procPath), "/proc/self/fd/%i", aFd) ==
42 (PRUint32)-1) {
43 return -1;
46 int32_t ret = readlink(procPath, aBuf, aBufSize - 1);
47 if (ret > -1) {
48 aBuf[ret] = '\0';
51 return ret;
52 #endif
55 /**
56 * RAII class for timing the duration of an NSPR I/O call and reporting the
57 * result to the mozilla::IOInterposeObserver API.
59 class NSPRIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
60 public:
61 explicit NSPRIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
62 PRFileDesc* aFd)
63 : mozilla::IOInterposeObserver::Observation(aOp, "NSPRIOInterposer") {
64 char filename[MAXPATHLEN];
65 if (mShouldReport && aFd &&
66 GetPathFromFd(PR_FileDesc2NativeHandle(aFd), filename,
67 sizeof(filename)) != -1) {
68 CopyUTF8toUTF16(mozilla::MakeStringSpan(filename), mFilename);
69 } else {
70 mFilename.Truncate();
74 void Filename(nsAString& aFilename) override { aFilename = mFilename; }
76 ~NSPRIOAutoObservation() override { Report(); }
78 private:
79 nsString mFilename;
82 PRStatus PR_CALLBACK interposedClose(PRFileDesc* aFd) {
83 // If we don't have a valid original function pointer something is very wrong.
84 NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL");
86 NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpClose, aFd);
87 return sCloseFn(aFd);
90 int32_t PR_CALLBACK interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt) {
91 // If we don't have a valid original function pointer something is very wrong.
92 NS_ASSERTION(sReadFn, "NSPR IO Interposing: sReadFn is NULL");
94 NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpRead, aFd);
95 return sReadFn(aFd, aBuf, aAmt);
98 int32_t PR_CALLBACK interposedWrite(PRFileDesc* aFd, const void* aBuf,
99 int32_t aAmt) {
100 // If we don't have a valid original function pointer something is very wrong.
101 NS_ASSERTION(sWriteFn, "NSPR IO Interposing: sWriteFn is NULL");
103 NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpWrite, aFd);
104 return sWriteFn(aFd, aBuf, aAmt);
107 PRStatus PR_CALLBACK interposedFSync(PRFileDesc* aFd) {
108 // If we don't have a valid original function pointer something is very wrong.
109 NS_ASSERTION(sFSyncFn, "NSPR IO Interposing: sFSyncFn is NULL");
111 NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpFSync, aFd);
112 return sFSyncFn(aFd);
115 PRStatus PR_CALLBACK interposedFileInfo(PRFileDesc* aFd, PRFileInfo* aInfo) {
116 // If we don't have a valid original function pointer something is very wrong.
117 NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL");
119 NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpStat, aFd);
120 return sFileInfoFn(aFd, aInfo);
123 PRStatus PR_CALLBACK interposedFileInfo64(PRFileDesc* aFd,
124 PRFileInfo64* aInfo) {
125 // If we don't have a valid original function pointer something is very wrong.
126 NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL");
128 NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpStat, aFd);
129 return sFileInfo64Fn(aFd, aInfo);
132 } // namespace
134 namespace mozilla {
136 void InitNSPRIOInterposing() {
137 // Check that we have not interposed any of the IO methods before
138 MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn &&
139 !sFileInfo64Fn);
141 // We can't actually use this assertion because we initialize this code
142 // before XPCOM is initialized, so NS_IsMainThread() always returns false.
143 // MOZ_ASSERT(NS_IsMainThread());
145 // Get IO methods from NSPR and const cast the structure so we can modify it.
146 PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
148 // Something is badly wrong if we don't get IO methods... However, we don't
149 // want to crash over that in non-debug builds. This is unlikely to happen
150 // so an assert is enough, no need to report it to the caller.
151 MOZ_ASSERT(methods);
152 if (!methods) {
153 return;
156 // Store original functions
157 sCloseFn = methods->close;
158 sReadFn = methods->read;
159 sWriteFn = methods->write;
160 sFSyncFn = methods->fsync;
161 sFileInfoFn = methods->fileInfo;
162 sFileInfo64Fn = methods->fileInfo64;
164 // Overwrite with our interposed functions
165 methods->close = &interposedClose;
166 methods->read = &interposedRead;
167 methods->write = &interposedWrite;
168 methods->fsync = &interposedFSync;
169 methods->fileInfo = &interposedFileInfo;
170 methods->fileInfo64 = &interposedFileInfo64;
173 void ClearNSPRIOInterposing() {
174 // If we have already cleared IO interposing, or not initialized it this is
175 // actually bad.
176 MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn &&
177 sFileInfo64Fn);
179 // Get IO methods from NSPR and const cast the structure so we can modify it.
180 PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
182 // Something is badly wrong if we don't get IO methods... However, we don't
183 // want to crash over that in non-debug builds. This is unlikely to happen
184 // so an assert is enough, no need to report it to the caller.
185 MOZ_ASSERT(methods);
186 if (!methods) {
187 return;
190 // Restore original functions
191 methods->close = sCloseFn;
192 methods->read = sReadFn;
193 methods->write = sWriteFn;
194 methods->fsync = sFSyncFn;
195 methods->fileInfo = sFileInfoFn;
196 methods->fileInfo64 = sFileInfo64Fn;
198 // Forget about original functions
199 sCloseFn = nullptr;
200 sReadFn = nullptr;
201 sWriteFn = nullptr;
202 sFSyncFn = nullptr;
203 sFileInfoFn = nullptr;
204 sFileInfo64Fn = nullptr;
207 } // namespace mozilla