|
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 |