xpcom/build/IOInterposer.cpp

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

mercurial