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/. */
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "IOInterposer.h"
11
12 #include "IOInterposerPrivate.h"
13 #include "MainThreadIOLogger.h"
14 #include "mozilla/Atomics.h"
15 #include "mozilla/Mutex.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/StaticPtr.h"
18 #include "mozilla/ThreadLocal.h"
19 #include "nscore.h" // for NS_FREE_PERMANENT_DATA
20 #if !defined(XP_WIN)
21 #include "NSPRInterposer.h"
22 #endif // !defined(XP_WIN)
23 #include "nsXULAppAPI.h"
24 #include "PoisonIOInterposer.h"
25
26 using namespace mozilla;
27
28 namespace {
29
30 /** Find if a vector contains a specific element */
31 template <class T>
VectorContains(const std::vector<T> & aVector,const T & aElement)32 bool VectorContains(const std::vector<T>& aVector, const T& aElement) {
33 return std::find(aVector.begin(), aVector.end(), aElement) != aVector.end();
34 }
35
36 /** Remove element from a vector */
37 template <class T>
VectorRemove(std::vector<T> & aVector,const T & aElement)38 void VectorRemove(std::vector<T>& aVector, const T& aElement) {
39 typename std::vector<T>::iterator newEnd =
40 std::remove(aVector.begin(), aVector.end(), aElement);
41 aVector.erase(newEnd, aVector.end());
42 }
43
44 /** Lists of Observers */
45 struct ObserverLists {
46 private:
~ObserverLists__anonab3aa38d0111::ObserverLists47 ~ObserverLists() {}
48
49 public:
50 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists)
51
ObserverLists__anonab3aa38d0111::ObserverLists52 ObserverLists() {}
53
ObserverLists__anonab3aa38d0111::ObserverLists54 ObserverLists(ObserverLists const& aOther)
55 : mCreateObservers(aOther.mCreateObservers),
56 mReadObservers(aOther.mReadObservers),
57 mWriteObservers(aOther.mWriteObservers),
58 mFSyncObservers(aOther.mFSyncObservers),
59 mStatObservers(aOther.mStatObservers),
60 mCloseObservers(aOther.mCloseObservers),
61 mStageObservers(aOther.mStageObservers) {}
62 // Lists of observers for I/O events.
63 // These are implemented as vectors since they are allowed to survive gecko,
64 // without reporting leaks. This is necessary for the IOInterposer to be used
65 // for late-write checks.
66 std::vector<IOInterposeObserver*> mCreateObservers;
67 std::vector<IOInterposeObserver*> mReadObservers;
68 std::vector<IOInterposeObserver*> mWriteObservers;
69 std::vector<IOInterposeObserver*> mFSyncObservers;
70 std::vector<IOInterposeObserver*> mStatObservers;
71 std::vector<IOInterposeObserver*> mCloseObservers;
72 std::vector<IOInterposeObserver*> mStageObservers;
73 };
74
75 class PerThreadData {
76 public:
PerThreadData(bool aIsMainThread=false)77 explicit PerThreadData(bool aIsMainThread = false)
78 : mIsMainThread(aIsMainThread),
79 mIsHandlingObservation(false),
80 mCurrentGeneration(0) {
81 MOZ_COUNT_CTOR(PerThreadData);
82 }
83
~PerThreadData()84 ~PerThreadData() { MOZ_COUNT_DTOR(PerThreadData); }
85
CallObservers(IOInterposeObserver::Observation & aObservation)86 void CallObservers(IOInterposeObserver::Observation& aObservation) {
87 // Prevent recursive reporting.
88 if (mIsHandlingObservation) {
89 return;
90 }
91
92 mIsHandlingObservation = true;
93 // Decide which list of observers to inform
94 std::vector<IOInterposeObserver*>* observers = nullptr;
95 switch (aObservation.ObservedOperation()) {
96 case IOInterposeObserver::OpCreateOrOpen:
97 observers = &mObserverLists->mCreateObservers;
98 break;
99 case IOInterposeObserver::OpRead:
100 observers = &mObserverLists->mReadObservers;
101 break;
102 case IOInterposeObserver::OpWrite:
103 observers = &mObserverLists->mWriteObservers;
104 break;
105 case IOInterposeObserver::OpFSync:
106 observers = &mObserverLists->mFSyncObservers;
107 break;
108 case IOInterposeObserver::OpStat:
109 observers = &mObserverLists->mStatObservers;
110 break;
111 case IOInterposeObserver::OpClose:
112 observers = &mObserverLists->mCloseObservers;
113 break;
114 case IOInterposeObserver::OpNextStage:
115 observers = &mObserverLists->mStageObservers;
116 break;
117 default: {
118 // Invalid IO operation, see documentation comment for
119 // IOInterposer::Report()
120 MOZ_ASSERT(false);
121 // Just ignore it in non-debug builds.
122 return;
123 }
124 }
125 MOZ_ASSERT(observers);
126
127 // Inform observers
128 for (auto i = observers->begin(), e = observers->end(); i != e; ++i) {
129 (*i)->Observe(aObservation);
130 }
131 mIsHandlingObservation = false;
132 }
133
GetCurrentGeneration() const134 inline uint32_t GetCurrentGeneration() const { return mCurrentGeneration; }
135
IsMainThread() const136 inline bool IsMainThread() const { return mIsMainThread; }
137
SetObserverLists(uint32_t aNewGeneration,RefPtr<ObserverLists> & aNewLists)138 inline void SetObserverLists(uint32_t aNewGeneration,
139 RefPtr<ObserverLists>& aNewLists) {
140 mCurrentGeneration = aNewGeneration;
141 mObserverLists = aNewLists;
142 }
143
ClearObserverLists()144 inline void ClearObserverLists() {
145 if (mObserverLists) {
146 mCurrentGeneration = 0;
147 mObserverLists = nullptr;
148 }
149 }
150
151 private:
152 bool mIsMainThread;
153 bool mIsHandlingObservation;
154 uint32_t mCurrentGeneration;
155 RefPtr<ObserverLists> mObserverLists;
156 };
157
158 class MasterList {
159 public:
MasterList()160 MasterList()
161 : mObservedOperations(IOInterposeObserver::OpNone), mIsEnabled(true) {
162 MOZ_COUNT_CTOR(MasterList);
163 }
164
~MasterList()165 ~MasterList() { MOZ_COUNT_DTOR(MasterList); }
166
Disable()167 inline void Disable() { mIsEnabled = false; }
Enable()168 inline void Enable() { mIsEnabled = true; }
169
Register(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)170 void Register(IOInterposeObserver::Operation aOp,
171 IOInterposeObserver* aObserver) {
172 IOInterposer::AutoLock lock(mLock);
173
174 ObserverLists* newLists = nullptr;
175 if (mObserverLists) {
176 newLists = new ObserverLists(*mObserverLists);
177 } else {
178 newLists = new ObserverLists();
179 }
180 // You can register to observe multiple types of observations
181 // but you'll never be registered twice for the same observations.
182 if (aOp & IOInterposeObserver::OpCreateOrOpen &&
183 !VectorContains(newLists->mCreateObservers, aObserver)) {
184 newLists->mCreateObservers.push_back(aObserver);
185 }
186 if (aOp & IOInterposeObserver::OpRead &&
187 !VectorContains(newLists->mReadObservers, aObserver)) {
188 newLists->mReadObservers.push_back(aObserver);
189 }
190 if (aOp & IOInterposeObserver::OpWrite &&
191 !VectorContains(newLists->mWriteObservers, aObserver)) {
192 newLists->mWriteObservers.push_back(aObserver);
193 }
194 if (aOp & IOInterposeObserver::OpFSync &&
195 !VectorContains(newLists->mFSyncObservers, aObserver)) {
196 newLists->mFSyncObservers.push_back(aObserver);
197 }
198 if (aOp & IOInterposeObserver::OpStat &&
199 !VectorContains(newLists->mStatObservers, aObserver)) {
200 newLists->mStatObservers.push_back(aObserver);
201 }
202 if (aOp & IOInterposeObserver::OpClose &&
203 !VectorContains(newLists->mCloseObservers, aObserver)) {
204 newLists->mCloseObservers.push_back(aObserver);
205 }
206 if (aOp & IOInterposeObserver::OpNextStage &&
207 !VectorContains(newLists->mStageObservers, aObserver)) {
208 newLists->mStageObservers.push_back(aObserver);
209 }
210 mObserverLists = newLists;
211 mObservedOperations =
212 (IOInterposeObserver::Operation)(mObservedOperations | aOp);
213
214 mCurrentGeneration++;
215 }
216
Unregister(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)217 void Unregister(IOInterposeObserver::Operation aOp,
218 IOInterposeObserver* aObserver) {
219 IOInterposer::AutoLock lock(mLock);
220
221 ObserverLists* newLists = nullptr;
222 if (mObserverLists) {
223 newLists = new ObserverLists(*mObserverLists);
224 } else {
225 newLists = new ObserverLists();
226 }
227
228 if (aOp & IOInterposeObserver::OpCreateOrOpen) {
229 VectorRemove(newLists->mCreateObservers, aObserver);
230 if (newLists->mCreateObservers.empty()) {
231 mObservedOperations = (IOInterposeObserver::Operation)(
232 mObservedOperations & ~IOInterposeObserver::OpCreateOrOpen);
233 }
234 }
235 if (aOp & IOInterposeObserver::OpRead) {
236 VectorRemove(newLists->mReadObservers, aObserver);
237 if (newLists->mReadObservers.empty()) {
238 mObservedOperations = (IOInterposeObserver::Operation)(
239 mObservedOperations & ~IOInterposeObserver::OpRead);
240 }
241 }
242 if (aOp & IOInterposeObserver::OpWrite) {
243 VectorRemove(newLists->mWriteObservers, aObserver);
244 if (newLists->mWriteObservers.empty()) {
245 mObservedOperations = (IOInterposeObserver::Operation)(
246 mObservedOperations & ~IOInterposeObserver::OpWrite);
247 }
248 }
249 if (aOp & IOInterposeObserver::OpFSync) {
250 VectorRemove(newLists->mFSyncObservers, aObserver);
251 if (newLists->mFSyncObservers.empty()) {
252 mObservedOperations = (IOInterposeObserver::Operation)(
253 mObservedOperations & ~IOInterposeObserver::OpFSync);
254 }
255 }
256 if (aOp & IOInterposeObserver::OpStat) {
257 VectorRemove(newLists->mStatObservers, aObserver);
258 if (newLists->mStatObservers.empty()) {
259 mObservedOperations = (IOInterposeObserver::Operation)(
260 mObservedOperations & ~IOInterposeObserver::OpStat);
261 }
262 }
263 if (aOp & IOInterposeObserver::OpClose) {
264 VectorRemove(newLists->mCloseObservers, aObserver);
265 if (newLists->mCloseObservers.empty()) {
266 mObservedOperations = (IOInterposeObserver::Operation)(
267 mObservedOperations & ~IOInterposeObserver::OpClose);
268 }
269 }
270 if (aOp & IOInterposeObserver::OpNextStage) {
271 VectorRemove(newLists->mStageObservers, aObserver);
272 if (newLists->mStageObservers.empty()) {
273 mObservedOperations = (IOInterposeObserver::Operation)(
274 mObservedOperations & ~IOInterposeObserver::OpNextStage);
275 }
276 }
277 mObserverLists = newLists;
278 mCurrentGeneration++;
279 }
280
Update(PerThreadData & aPtd)281 void Update(PerThreadData& aPtd) {
282 if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
283 return;
284 }
285 // If the generation counts don't match then we need to update the current
286 // thread's observer list with the new master list.
287 IOInterposer::AutoLock lock(mLock);
288 aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
289 }
290
IsObservedOperation(IOInterposeObserver::Operation aOp)291 inline bool IsObservedOperation(IOInterposeObserver::Operation aOp) {
292 // The quick reader may observe that no locks are being employed here,
293 // hence the result of the operations is truly undefined. However, most
294 // computers will usually return either true or false, which is good enough.
295 // It is not a problem if we occasionally report more or less IO than is
296 // actually occurring.
297 return mIsEnabled && !!(mObservedOperations & aOp);
298 }
299
300 private:
301 RefPtr<ObserverLists> mObserverLists;
302 // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked
303 // (We want to monitor IO during shutdown). Furthermore, as we may have to
304 // unregister observers during shutdown an OffTheBooksMutex is not an option
305 // either, as its base calls into sDeadlockDetector which may be nullptr
306 // during shutdown.
307 IOInterposer::Mutex mLock;
308 // Flags tracking which operations are being observed
309 IOInterposeObserver::Operation mObservedOperations;
310 // Used for quickly disabling everything by IOInterposer::Disable()
311 Atomic<bool> mIsEnabled;
312 // Used to inform threads that the master observer list has changed
313 Atomic<uint32_t> mCurrentGeneration;
314 };
315
316 // Special observation used by IOInterposer::EnteringNextStage()
317 class NextStageObservation : public IOInterposeObserver::Observation {
318 public:
NextStageObservation()319 NextStageObservation()
320 : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
321 "IOInterposer", false) {
322 mStart = TimeStamp::Now();
323 mEnd = mStart;
324 }
325 };
326
327 // List of observers registered
328 static StaticAutoPtr<MasterList> sMasterList;
329 static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
330 static bool sThreadLocalDataInitialized;
331 } // namespace
332
Observation(Operation aOperation,const char * aReference,bool aShouldReport)333 IOInterposeObserver::Observation::Observation(Operation aOperation,
334 const char* aReference,
335 bool aShouldReport)
336 : mOperation(aOperation),
337 mReference(aReference),
338 mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
339 aShouldReport) {
340 if (mShouldReport) {
341 mStart = TimeStamp::Now();
342 }
343 }
344
Observation(Operation aOperation,const TimeStamp & aStart,const TimeStamp & aEnd,const char * aReference)345 IOInterposeObserver::Observation::Observation(Operation aOperation,
346 const TimeStamp& aStart,
347 const TimeStamp& aEnd,
348 const char* aReference)
349 : mOperation(aOperation),
350 mStart(aStart),
351 mEnd(aEnd),
352 mReference(aReference),
353 mShouldReport(false) {}
354
ObservedOperationString() const355 const char* IOInterposeObserver::Observation::ObservedOperationString() const {
356 switch (mOperation) {
357 case OpCreateOrOpen:
358 return "create/open";
359 case OpRead:
360 return "read";
361 case OpWrite:
362 return "write";
363 case OpFSync:
364 return "fsync";
365 case OpStat:
366 return "stat";
367 case OpClose:
368 return "close";
369 case OpNextStage:
370 return "NextStage";
371 default:
372 return "unknown";
373 }
374 }
375
Report()376 void IOInterposeObserver::Observation::Report() {
377 if (mShouldReport) {
378 mEnd = TimeStamp::Now();
379 IOInterposer::Report(*this);
380 }
381 }
382
Init()383 bool IOInterposer::Init() {
384 // Don't initialize twice...
385 if (sMasterList) {
386 return true;
387 }
388 if (!sThreadLocalData.init()) {
389 return false;
390 }
391 sThreadLocalDataInitialized = true;
392 bool isMainThread = true;
393 RegisterCurrentThread(isMainThread);
394 sMasterList = new MasterList();
395
396 MainThreadIOLogger::Init();
397
398 // Now we initialize the various interposers depending on platform
399 InitPoisonIOInterposer();
400 // We don't hook NSPR on Windows because PoisonIOInterposer captures a
401 // superset of the former's events.
402 #if !defined(XP_WIN)
403 InitNSPRIOInterposing();
404 #endif
405 return true;
406 }
407
IsMainThread()408 bool IOInterposeObserver::IsMainThread() {
409 if (!sThreadLocalDataInitialized) {
410 return false;
411 }
412 PerThreadData* ptd = sThreadLocalData.get();
413 if (!ptd) {
414 return false;
415 }
416 return ptd->IsMainThread();
417 }
418
Clear()419 void IOInterposer::Clear() {
420 /* Clear() is a no-op on release builds so that we may continue to trap I/O
421 until process termination. In leak-checking builds, we need to shut down
422 IOInterposer so that all references are properly released. */
423 #ifdef NS_FREE_PERMANENT_DATA
424 UnregisterCurrentThread();
425 sMasterList = nullptr;
426 #endif
427 }
428
Disable()429 void IOInterposer::Disable() {
430 if (!sMasterList) {
431 return;
432 }
433 sMasterList->Disable();
434 }
435
Enable()436 void IOInterposer::Enable() {
437 if (!sMasterList) {
438 return;
439 }
440 sMasterList->Enable();
441 }
442
Report(IOInterposeObserver::Observation & aObservation)443 void IOInterposer::Report(IOInterposeObserver::Observation& aObservation) {
444 PerThreadData* ptd = sThreadLocalData.get();
445 if (!ptd) {
446 // In this case the current thread is not registered with IOInterposer.
447 // Alternatively we could take the slow path and just lock everything if
448 // we're not registered. That could potentially perform poorly, though.
449 return;
450 }
451
452 if (!sMasterList) {
453 // If there is no longer a master list then we should clear the local one.
454 ptd->ClearObserverLists();
455 return;
456 }
457
458 sMasterList->Update(*ptd);
459
460 // Don't try to report if there's nobody listening.
461 if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
462 return;
463 }
464
465 ptd->CallObservers(aObservation);
466 }
467
IsObservedOperation(IOInterposeObserver::Operation aOp)468 bool IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp) {
469 return sMasterList && sMasterList->IsObservedOperation(aOp);
470 }
471
Register(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)472 void IOInterposer::Register(IOInterposeObserver::Operation aOp,
473 IOInterposeObserver* aObserver) {
474 MOZ_ASSERT(aObserver);
475 if (!sMasterList || !aObserver) {
476 return;
477 }
478
479 sMasterList->Register(aOp, aObserver);
480 }
481
Unregister(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)482 void IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
483 IOInterposeObserver* aObserver) {
484 if (!sMasterList) {
485 return;
486 }
487
488 sMasterList->Unregister(aOp, aObserver);
489 }
490
RegisterCurrentThread(bool aIsMainThread)491 void IOInterposer::RegisterCurrentThread(bool aIsMainThread) {
492 if (!sThreadLocalDataInitialized) {
493 return;
494 }
495 MOZ_ASSERT(!sThreadLocalData.get());
496 PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
497 sThreadLocalData.set(curThreadData);
498 }
499
UnregisterCurrentThread()500 void IOInterposer::UnregisterCurrentThread() {
501 if (!sThreadLocalDataInitialized) {
502 return;
503 }
504 PerThreadData* curThreadData = sThreadLocalData.get();
505 MOZ_ASSERT(curThreadData);
506 sThreadLocalData.set(nullptr);
507 delete curThreadData;
508 }
509
EnteringNextStage()510 void IOInterposer::EnteringNextStage() {
511 if (!sMasterList) {
512 return;
513 }
514 NextStageObservation observation;
515 Report(observation);
516 }
517