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