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