Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/build/IOInterposer.h
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
#ifndef mozilla_IOInterposer_h
8
#define mozilla_IOInterposer_h
9
10
#include "mozilla/Attributes.h"
11
#include "mozilla/GuardObjects.h"
12
#include "mozilla/TimeStamp.h"
13
#include "nsString.h"
14
15
namespace mozilla {
16
17
/**
18
 * Interface for I/O interposer observers. This is separate from the
19
 * IOInterposer because we have multiple uses for these observations.
20
 */
21
class IOInterposeObserver
22
{
23
public:
24
  enum Operation
25
  {
26
    OpNone = 0,
27
    OpCreateOrOpen = (1 << 0),
28
    OpRead = (1 << 1),
29
    OpWrite = (1 << 2),
30
    OpFSync = (1 << 3),
31
    OpStat = (1 << 4),
32
    OpClose = (1 << 5),
33
    OpNextStage = (1 << 6), // Meta - used when leaving startup, entering shutdown
34
    OpWriteFSync = (OpWrite | OpFSync),
35
    OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose),
36
    OpAllWithStaging = (OpAll | OpNextStage)
37
  };
38
39
  /** A representation of an I/O observation  */
40
  class Observation
41
  {
42
  protected:
43
    /**
44
     * This constructor is for use by subclasses that are intended to take
45
     * timing measurements via RAII. The |aShouldReport| parameter may be
46
     * used to make the measurement and reporting conditional on the
47
     * satisfaction of an arbitrary predicate that was evaluated
48
     * in the subclass. Note that IOInterposer::IsObservedOperation() is
49
     * always ANDed with aShouldReport, so the subclass does not need to
50
     * include a call to that function explicitly.
51
     */
52
    Observation(Operation aOperation, const char* aReference,
53
                bool aShouldReport = true);
54
55
  public:
56
    /**
57
     * Since this constructor accepts start and end times, it does *not* take
58
     * its own timings, nor does it report itself.
59
     */
60
    Observation(Operation aOperation, const TimeStamp& aStart,
61
                const TimeStamp& aEnd, const char* aReference);
62
63
    /**
64
     * Operation observed, this is one of the individual Operation values.
65
     * Combinations of these flags are only used when registering observers.
66
     */
67
    Operation ObservedOperation() const { return mOperation; }
68
69
    /**
70
     * Return the observed operation as a human-readable string.
71
     */
72
    const char* ObservedOperationString() const;
73
74
    /** Time at which the I/O operation was started */
75
    TimeStamp Start() const { return mStart; }
76
77
    /**
78
     * Time at which the I/O operation ended, for asynchronous methods this is
79
     * the time at which the call initiating the asynchronous request returned.
80
     */
81
    TimeStamp End() const { return mEnd; }
82
83
    /**
84
     * Duration of the operation, for asynchronous I/O methods this is the
85
     * duration of the call initiating the asynchronous request.
86
     */
87
    TimeDuration Duration() const { return mEnd - mStart; }
88
89
    /**
90
     * IO reference, function name or name of component (sqlite) that did IO
91
     * this is in addition the generic operation. This attribute may be platform
92
     * specific, but should only take a finite number of distinct values.
93
     * E.g. sqlite-commit, CreateFile, NtReadFile, fread, fsync, mmap, etc.
94
     * I.e. typically the platform specific function that did the IO.
95
     */
96
    const char* Reference() const { return mReference; }
97
98
    /** Request filename associated with the I/O operation, empty if unknown */
99
0
    virtual void Filename(nsAString& aString) { aString.Truncate(); }
100
101
54
    virtual ~Observation() {}
102
103
  protected:
104
    void
105
    Report();
106
107
    Operation   mOperation;
108
    TimeStamp   mStart;
109
    TimeStamp   mEnd;
110
    const char* mReference;     // Identifies the source of the Observation
111
    bool        mShouldReport;  // Measure and report if true
112
  };
113
114
  /**
115
   * Invoked whenever an implementation of the IOInterposeObserver should
116
   * observe aObservation. Implement this and do your thing...
117
   * But do consider if it is wise to use IO functions in this method, they are
118
   * likely to cause recursion :)
119
   * At least, see PoisonIOInterposer.h and register your handle as a debug file
120
   * even, if you don't initialize the poison IO interposer, someone else might.
121
   *
122
   * Remark: Observations may occur on any thread.
123
   */
124
  virtual void Observe(Observation& aObservation) = 0;
125
126
  virtual ~IOInterposeObserver() {}
127
128
protected:
129
  /**
130
   * We don't use NS_IsMainThread() because we need to be able to determine the
131
   * main thread outside of XPCOM Initialization. IOInterposer observers should
132
   * call this function instead.
133
   */
134
  static bool IsMainThread();
135
};
136
137
/**
138
 * These functions are responsible for ensuring that events are routed to the
139
 * appropriate observers.
140
 */
141
namespace IOInterposer {
142
143
/**
144
 * This function must be called from the main-thread when no other threads are
145
 * running before any of the other methods on this class may be used.
146
 *
147
 * IO reports can however, safely assume that IsObservedOperation() will
148
 * return false until the IOInterposer is initialized.
149
 *
150
 * Remark, it's safe to call this method multiple times, so just call it when
151
 * you to utilize IO interposing.
152
 *
153
 * Using the IOInterposerInit class is preferred to calling this directly.
154
 */
155
bool Init();
156
157
/**
158
 * This function must be called from the main thread, and furthermore
159
 * it must be called when no other threads are executing. Effectively
160
 * restricting us to calling it only during shutdown.
161
 *
162
 * Callers should take care that no other consumers are subscribed to events,
163
 * as these events will stop when this function is called.
164
 *
165
 * In practice, we don't use this method as the IOInterposer is used for
166
 * late-write checks.
167
 */
168
void Clear();
169
170
/**
171
 * This function immediately disables IOInterposer functionality in a fast,
172
 * thread-safe manner. Primarily for use by the crash reporter.
173
 */
174
void Disable();
175
176
/**
177
 * This function re-enables IOInterposer functionality in a fast, thread-safe
178
 * manner.  Primarily for use by the crash reporter.
179
 */
180
void Enable();
181
182
/**
183
 * Report IO to registered observers.
184
 * Notice that the reported operation must be either OpRead, OpWrite or
185
 * OpFSync. You are not allowed to report an observation with OpWriteFSync or
186
 * OpAll, these are just auxiliary values for use with Register().
187
 *
188
 * If the IO call you're reporting does multiple things, write and fsync, you
189
 * can choose to call Report() twice once with write and once with FSync. You
190
 * may not call Report() with OpWriteFSync! The Observation::mOperation
191
 * attribute is meant to be generic, not perfect.
192
 *
193
 * Notice that there is no reason to report an observation with an operation
194
 * which is not being observed. Use IsObservedOperation() to check if the
195
 * operation you are about to report is being observed. This is especially
196
 * important if you are constructing expensive observations containing
197
 * filename and full-path.
198
 *
199
 * Remark: Init() must be called before any IO is reported. But
200
 * IsObservedOperation() will return false until Init() is called.
201
 */
202
void Report(IOInterposeObserver::Observation& aObservation);
203
204
/**
205
 * Return whether or not an operation is observed. Reporters should not
206
 * report operations that are not being observed by anybody. This mechanism
207
 * allows us to avoid reporting I/O when no observers are registered.
208
 */
209
bool IsObservedOperation(IOInterposeObserver::Operation aOp);
210
211
/**
212
 * Register IOInterposeObserver, the observer object will receive all
213
 * observations for the given operation aOp.
214
 *
215
 * Remark: Init() must be called before observers are registered.
216
 */
217
void Register(IOInterposeObserver::Operation aOp,
218
              IOInterposeObserver* aObserver);
219
220
/**
221
 * Unregister an IOInterposeObserver for a given operation
222
 * Remark: It is always safe to unregister for all operations, even if yoú
223
 * didn't register for them all.
224
 * I.e. IOInterposer::Unregister(IOInterposeObserver::OpAll, aObserver)
225
 *
226
 * Remark: Init() must be called before observers are unregistered.
227
 */
228
void Unregister(IOInterposeObserver::Operation aOp,
229
                IOInterposeObserver* aObserver);
230
231
/**
232
 * Registers the current thread with the IOInterposer. This must be done to
233
 * ensure that per-thread data is created in an orderly fashion.
234
 * We could have written this to initialize that data lazily, however this
235
 * could have unintended consequences if a thread that is not aware of
236
 * IOInterposer was implicitly registered: its per-thread data would never
237
 * be deleted because it would not know to unregister itself.
238
 *
239
 * @param aIsMainThread true if IOInterposer should treat the current thread
240
 *                      as the main thread.
241
 */
242
void RegisterCurrentThread(bool aIsMainThread = false);
243
244
/**
245
 * Unregisters the current thread with the IOInterposer. This is important
246
 * to call when a thread is shutting down because it cleans up data that
247
 * is stored in a TLS slot.
248
 */
249
void UnregisterCurrentThread();
250
251
/**
252
 * Called to inform observers that the process has transitioned out of the
253
 * startup stage or into the shutdown stage. Main thread only.
254
 */
255
void EnteringNextStage();
256
257
} // namespace IOInterposer
258
259
class IOInterposerInit
260
{
261
public:
262
  IOInterposerInit()
263
  {
264
#if !defined(RELEASE_OR_BETA)
265
    IOInterposer::Init();
266
#endif
267
  }
268
269
  ~IOInterposerInit()
270
  {
271
#if !defined(RELEASE_OR_BETA)
272
    IOInterposer::Clear();
273
#endif
274
  }
275
};
276
277
class MOZ_RAII AutoIOInterposerDisable final
278
{
279
public:
280
  explicit AutoIOInterposerDisable(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
281
  {
282
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
283
    IOInterposer::Disable();
284
  }
285
  ~AutoIOInterposerDisable()
286
  {
287
    IOInterposer::Enable();
288
  }
289
290
private:
291
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
292
};
293
294
} // namespace mozilla
295
296
#endif // mozilla_IOInterposer_h