Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/build/IOInterposer.cpp
Line
Count
Source (jump to first uncovered line)
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
VectorContains(const std::vector<T>& aVector, const T& aElement)
34
21
{
35
21
  return std::find(aVector.begin(), aVector.end(), aElement) != aVector.end();
36
21
}
37
38
/** Remove element from a vector */
39
template<class T>
40
void
41
VectorRemove(std::vector<T>& aVector, const T& aElement)
42
0
{
43
0
  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
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists)
56
57
3
  ObserverLists() {}
58
59
  ObserverLists(ObserverLists const& aOther)
60
    : mCreateObservers(aOther.mCreateObservers)
61
    , mReadObservers(aOther.mReadObservers)
62
    , mWriteObservers(aOther.mWriteObservers)
63
    , mFSyncObservers(aOther.mFSyncObservers)
64
    , mStatObservers(aOther.mStatObservers)
65
    , mCloseObservers(aOther.mCloseObservers)
66
    , mStageObservers(aOther.mStageObservers)
67
0
  {
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
  explicit PerThreadData(bool aIsMainThread = false)
86
    : mIsMainThread(aIsMainThread)
87
    , mIsHandlingObservation(false)
88
    , mCurrentGeneration(0)
89
19
  {
90
19
    MOZ_COUNT_CTOR(PerThreadData);
91
19
  }
92
93
  ~PerThreadData()
94
0
  {
95
0
    MOZ_COUNT_DTOR(PerThreadData);
96
0
  }
97
98
  void CallObservers(IOInterposeObserver::Observation& aObservation)
99
54
  {
100
54
    // Prevent recursive reporting.
101
54
    if (mIsHandlingObservation) {
102
0
      return;
103
0
    }
104
54
105
54
    mIsHandlingObservation = true;
106
54
    // Decide which list of observers to inform
107
54
    std::vector<IOInterposeObserver*>* observers = nullptr;
108
54
    switch (aObservation.ObservedOperation()) {
109
54
      case IOInterposeObserver::OpCreateOrOpen:
110
0
        observers = &mObserverLists->mCreateObservers;
111
0
        break;
112
54
      case IOInterposeObserver::OpRead:
113
27
        observers = &mObserverLists->mReadObservers;
114
27
        break;
115
54
      case IOInterposeObserver::OpWrite:
116
0
        observers = &mObserverLists->mWriteObservers;
117
0
        break;
118
54
      case IOInterposeObserver::OpFSync:
119
0
        observers = &mObserverLists->mFSyncObservers;
120
0
        break;
121
54
      case IOInterposeObserver::OpStat:
122
9
        observers = &mObserverLists->mStatObservers;
123
9
        break;
124
54
      case IOInterposeObserver::OpClose:
125
18
        observers = &mObserverLists->mCloseObservers;
126
18
        break;
127
54
      case IOInterposeObserver::OpNextStage:
128
0
        observers = &mObserverLists->mStageObservers;
129
0
        break;
130
54
      default: {
131
0
        // Invalid IO operation, see documentation comment for
132
0
        // IOInterposer::Report()
133
0
        MOZ_ASSERT(false);
134
0
        // Just ignore it in non-debug builds.
135
0
        return;
136
54
      }
137
54
    }
138
54
    MOZ_ASSERT(observers);
139
54
140
54
    // Inform observers
141
108
    for (auto i = observers->begin(), e = observers->end(); i != e; ++i) {
142
54
      (*i)->Observe(aObservation);
143
54
    }
144
54
    mIsHandlingObservation = false;
145
54
  }
146
147
54
  inline uint32_t GetCurrentGeneration() const { return mCurrentGeneration; }
148
149
54
  inline bool IsMainThread() const { return mIsMainThread; }
150
151
  inline void SetObserverLists(uint32_t aNewGeneration,
152
                               RefPtr<ObserverLists>& aNewLists)
153
3
  {
154
3
    mCurrentGeneration = aNewGeneration;
155
3
    mObserverLists = aNewLists;
156
3
  }
157
158
  inline void ClearObserverLists()
159
0
  {
160
0
    if (mObserverLists) {
161
0
      mCurrentGeneration = 0;
162
0
      mObserverLists = nullptr;
163
0
    }
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
  MasterList()
177
    : mObservedOperations(IOInterposeObserver::OpNone)
178
    , mIsEnabled(true)
179
3
  {
180
3
    MOZ_COUNT_CTOR(MasterList);
181
3
  }
182
183
  ~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
  void Register(IOInterposeObserver::Operation aOp,
192
                IOInterposeObserver* aObserver)
193
3
  {
194
3
    IOInterposer::AutoLock lock(mLock);
195
3
196
3
    ObserverLists* newLists = nullptr;
197
3
    if (mObserverLists) {
198
0
      newLists = new ObserverLists(*mObserverLists);
199
3
    } else {
200
3
      newLists = new ObserverLists();
201
3
    }
202
3
    // You can register to observe multiple types of observations
203
3
    // but you'll never be registered twice for the same observations.
204
3
    if (aOp & IOInterposeObserver::OpCreateOrOpen &&
205
3
        !VectorContains(newLists->mCreateObservers, aObserver)) {
206
3
      newLists->mCreateObservers.push_back(aObserver);
207
3
    }
208
3
    if (aOp & IOInterposeObserver::OpRead &&
209
3
        !VectorContains(newLists->mReadObservers, aObserver)) {
210
3
      newLists->mReadObservers.push_back(aObserver);
211
3
    }
212
3
    if (aOp & IOInterposeObserver::OpWrite &&
213
3
        !VectorContains(newLists->mWriteObservers, aObserver)) {
214
3
      newLists->mWriteObservers.push_back(aObserver);
215
3
    }
216
3
    if (aOp & IOInterposeObserver::OpFSync &&
217
3
        !VectorContains(newLists->mFSyncObservers, aObserver)) {
218
3
      newLists->mFSyncObservers.push_back(aObserver);
219
3
    }
220
3
    if (aOp & IOInterposeObserver::OpStat &&
221
3
        !VectorContains(newLists->mStatObservers, aObserver)) {
222
3
      newLists->mStatObservers.push_back(aObserver);
223
3
    }
224
3
    if (aOp & IOInterposeObserver::OpClose &&
225
3
        !VectorContains(newLists->mCloseObservers, aObserver)) {
226
3
      newLists->mCloseObservers.push_back(aObserver);
227
3
    }
228
3
    if (aOp & IOInterposeObserver::OpNextStage &&
229
3
        !VectorContains(newLists->mStageObservers, aObserver)) {
230
3
      newLists->mStageObservers.push_back(aObserver);
231
3
    }
232
3
    mObserverLists = newLists;
233
3
    mObservedOperations =
234
3
      (IOInterposeObserver::Operation)(mObservedOperations | aOp);
235
3
236
3
    mCurrentGeneration++;
237
3
  }
238
239
  void Unregister(IOInterposeObserver::Operation aOp,
240
                  IOInterposeObserver* aObserver)
241
0
  {
242
0
    IOInterposer::AutoLock lock(mLock);
243
0
244
0
    ObserverLists* newLists = nullptr;
245
0
    if (mObserverLists) {
246
0
      newLists = new ObserverLists(*mObserverLists);
247
0
    } else {
248
0
      newLists = new ObserverLists();
249
0
    }
250
0
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
0
                                           ~IOInterposeObserver::OpCreateOrOpen);
257
0
      }
258
0
    }
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
0
                                           ~IOInterposeObserver::OpRead);
265
0
      }
266
0
    }
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
0
                                           ~IOInterposeObserver::OpWrite);
273
0
      }
274
0
    }
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
0
                                           ~IOInterposeObserver::OpFSync);
281
0
      }
282
0
    }
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
0
                                           ~IOInterposeObserver::OpStat);
289
0
      }
290
0
    }
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
0
                                           ~IOInterposeObserver::OpClose);
297
0
      }
298
0
    }
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
0
                                           ~IOInterposeObserver::OpNextStage);
305
0
      }
306
0
    }
307
0
    mObserverLists = newLists;
308
0
    mCurrentGeneration++;
309
0
  }
310
311
  void Update(PerThreadData& aPtd)
312
54
  {
313
54
    if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
314
51
      return;
315
51
    }
316
3
    // If the generation counts don't match then we need to update the current
317
3
    // thread's observer list with the new master list.
318
3
    IOInterposer::AutoLock lock(mLock);
319
3
    aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
320
3
  }
321
322
  inline bool IsObservedOperation(IOInterposeObserver::Operation aOp)
323
108
  {
324
108
    // The quick reader may observe that no locks are being employed here,
325
108
    // hence the result of the operations is truly undefined. However, most
326
108
    // computers will usually return either true or false, which is good enough.
327
108
    // It is not a problem if we occasionally report more or less IO than is
328
108
    // actually occurring.
329
108
    return mIsEnabled && !!(mObservedOperations & aOp);
330
108
  }
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
class NextStageObservation : public IOInterposeObserver::Observation
350
{
351
public:
352
  NextStageObservation()
353
    : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
354
                                       "IOInterposer", false)
355
0
  {
356
0
    mStart = TimeStamp::Now();
357
0
    mEnd = mStart;
358
0
  }
359
};
360
361
// List of observers registered
362
static StaticAutoPtr<MasterList> sMasterList;
363
static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
364
static bool sThreadLocalDataInitialized;
365
} // namespace
366
367
IOInterposeObserver::Observation::Observation(Operation aOperation,
368
                                              const char* aReference,
369
                                              bool aShouldReport)
370
  : mOperation(aOperation)
371
  , mReference(aReference)
372
  , mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
373
                  aShouldReport)
374
54
{
375
54
  if (mShouldReport) {
376
54
    mStart = TimeStamp::Now();
377
54
  }
378
54
}
379
380
IOInterposeObserver::Observation::Observation(Operation aOperation,
381
                                              const TimeStamp& aStart,
382
                                              const TimeStamp& aEnd,
383
                                              const char* aReference)
384
  : mOperation(aOperation)
385
  , mStart(aStart)
386
  , mEnd(aEnd)
387
  , mReference(aReference)
388
  , mShouldReport(false)
389
0
{
390
0
}
391
392
const char*
393
IOInterposeObserver::Observation::ObservedOperationString() const
394
{
395
  switch (mOperation) {
396
    case OpCreateOrOpen:
397
      return "create/open";
398
    case OpRead:
399
      return "read";
400
    case OpWrite:
401
      return "write";
402
    case OpFSync:
403
      return "fsync";
404
    case OpStat:
405
      return "stat";
406
    case OpClose:
407
      return "close";
408
    case OpNextStage:
409
      return "NextStage";
410
    default:
411
      return "unknown";
412
  }
413
}
414
415
void
416
IOInterposeObserver::Observation::Report()
417
54
{
418
54
  if (mShouldReport) {
419
54
    mEnd = TimeStamp::Now();
420
54
    IOInterposer::Report(*this);
421
54
  }
422
54
}
423
424
bool
425
IOInterposer::Init()
426
3
{
427
3
  // Don't initialize twice...
428
3
  if (sMasterList) {
429
0
    return true;
430
0
  }
431
3
  if (!sThreadLocalData.init()) {
432
0
    return false;
433
0
  }
434
3
  sThreadLocalDataInitialized = true;
435
3
  bool isMainThread = true;
436
3
  RegisterCurrentThread(isMainThread);
437
3
  sMasterList = new MasterList();
438
3
439
3
  MainThreadIOLogger::Init();
440
3
441
3
  // Now we initialize the various interposers depending on platform
442
3
  InitPoisonIOInterposer();
443
3
  // We don't hook NSPR on Windows because PoisonIOInterposer captures a
444
3
  // superset of the former's events.
445
3
#if !defined(XP_WIN)
446
3
  InitNSPRIOInterposing();
447
3
#endif
448
3
  return true;
449
3
}
450
451
bool
452
IOInterposeObserver::IsMainThread()
453
54
{
454
54
  if (!sThreadLocalDataInitialized) {
455
0
    return false;
456
0
  }
457
54
  PerThreadData* ptd = sThreadLocalData.get();
458
54
  if (!ptd) {
459
0
    return false;
460
0
  }
461
54
  return ptd->IsMainThread();
462
54
}
463
464
void
465
IOInterposer::Clear()
466
0
{
467
0
  /* Clear() is a no-op on release builds so that we may continue to trap I/O
468
0
     until process termination. In leak-checking builds, we need to shut down
469
0
     IOInterposer so that all references are properly released. */
470
#ifdef NS_FREE_PERMANENT_DATA
471
  UnregisterCurrentThread();
472
  sMasterList = nullptr;
473
#endif
474
}
475
476
void
477
IOInterposer::Disable()
478
0
{
479
0
  if (!sMasterList) {
480
0
    return;
481
0
  }
482
0
  sMasterList->Disable();
483
0
}
484
485
void
486
IOInterposer::Enable()
487
0
{
488
0
  if (!sMasterList) {
489
0
    return;
490
0
  }
491
0
  sMasterList->Enable();
492
0
}
493
494
void
495
IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
496
54
{
497
54
  PerThreadData* ptd = sThreadLocalData.get();
498
54
  if (!ptd) {
499
0
    // In this case the current thread is not registered with IOInterposer.
500
0
    // Alternatively we could take the slow path and just lock everything if
501
0
    // we're not registered. That could potentially perform poorly, though.
502
0
    return;
503
0
  }
504
54
505
54
  if (!sMasterList) {
506
0
    // If there is no longer a master list then we should clear the local one.
507
0
    ptd->ClearObserverLists();
508
0
    return;
509
0
  }
510
54
511
54
  sMasterList->Update(*ptd);
512
54
513
54
  // Don't try to report if there's nobody listening.
514
54
  if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
515
0
    return;
516
0
  }
517
54
518
54
  ptd->CallObservers(aObservation);
519
54
}
520
521
bool
522
IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
523
108
{
524
108
  return sMasterList && sMasterList->IsObservedOperation(aOp);
525
108
}
526
527
void
528
IOInterposer::Register(IOInterposeObserver::Operation aOp,
529
                       IOInterposeObserver* aObserver)
530
3
{
531
3
  MOZ_ASSERT(aObserver);
532
3
  if (!sMasterList || !aObserver) {
533
0
    return;
534
0
  }
535
3
536
3
  sMasterList->Register(aOp, aObserver);
537
3
}
538
539
void
540
IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
541
                         IOInterposeObserver* aObserver)
542
0
{
543
0
  if (!sMasterList) {
544
0
    return;
545
0
  }
546
0
547
0
  sMasterList->Unregister(aOp, aObserver);
548
0
}
549
550
void
551
IOInterposer::RegisterCurrentThread(bool aIsMainThread)
552
19
{
553
19
  if (!sThreadLocalDataInitialized) {
554
0
    return;
555
0
  }
556
19
  MOZ_ASSERT(!sThreadLocalData.get());
557
19
  PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
558
19
  sThreadLocalData.set(curThreadData);
559
19
}
560
561
void
562
IOInterposer::UnregisterCurrentThread()
563
0
{
564
0
  if (!sThreadLocalDataInitialized) {
565
0
    return;
566
0
  }
567
0
  PerThreadData* curThreadData = sThreadLocalData.get();
568
0
  MOZ_ASSERT(curThreadData);
569
0
  sThreadLocalData.set(nullptr);
570
0
  delete curThreadData;
571
0
}
572
573
void
574
IOInterposer::EnteringNextStage()
575
0
{
576
0
  if (!sMasterList) {
577
0
    return;
578
0
  }
579
0
  NextStageObservation observation;
580
0
  Report(observation);
581
0
}
582