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