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