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