DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (3dc70a33491f)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_IOInterposer_h
#define mozilla_IOInterposer_h

#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/TimeStamp.h"
#include "nsString.h"

namespace mozilla {

/**
 * Interface for I/O interposer observers. This is separate from the
 * IOInterposer because we have multiple uses for these observations.
 */
class IOInterposeObserver {
 public:
  enum Operation {
    OpNone = 0,
    OpCreateOrOpen = (1 << 0),
    OpRead = (1 << 1),
    OpWrite = (1 << 2),
    OpFSync = (1 << 3),
    OpStat = (1 << 4),
    OpClose = (1 << 5),
    OpNextStage =
        (1 << 6),  // Meta - used when leaving startup, entering shutdown
    OpWriteFSync = (OpWrite | OpFSync),
    OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose),
    OpAllWithStaging = (OpAll | OpNextStage)
  };

  /** A representation of an I/O observation  */
  class Observation {
   protected:
    /**
     * This constructor is for use by subclasses that are intended to take
     * timing measurements via RAII. The |aShouldReport| parameter may be
     * used to make the measurement and reporting conditional on the
     * satisfaction of an arbitrary predicate that was evaluated
     * in the subclass. Note that IOInterposer::IsObservedOperation() is
     * always ANDed with aShouldReport, so the subclass does not need to
     * include a call to that function explicitly.
     */
    Observation(Operation aOperation, const char* aReference,
                bool aShouldReport = true);

   public:
    /**
     * Since this constructor accepts start and end times, it does *not* take
     * its own timings, nor does it report itself.
     */
    Observation(Operation aOperation, const TimeStamp& aStart,
                const TimeStamp& aEnd, const char* aReference);

    /**
     * Operation observed, this is one of the individual Operation values.
     * Combinations of these flags are only used when registering observers.
     */
    Operation ObservedOperation() const { return mOperation; }

    /**
     * Return the observed operation as a human-readable string.
     */
    const char* ObservedOperationString() const;

    /** Time at which the I/O operation was started */
    TimeStamp Start() const { return mStart; }

    /**
     * Time at which the I/O operation ended, for asynchronous methods this is
     * the time at which the call initiating the asynchronous request returned.
     */
    TimeStamp End() const { return mEnd; }

    /**
     * Duration of the operation, for asynchronous I/O methods this is the
     * duration of the call initiating the asynchronous request.
     */
    TimeDuration Duration() const { return mEnd - mStart; }

    /**
     * IO reference, function name or name of component (sqlite) that did IO
     * this is in addition the generic operation. This attribute may be platform
     * specific, but should only take a finite number of distinct values.
     * E.g. sqlite-commit, CreateFile, NtReadFile, fread, fsync, mmap, etc.
     * I.e. typically the platform specific function that did the IO.
     */
    const char* Reference() const { return mReference; }

    /** Request filename associated with the I/O operation, empty if unknown */
    virtual void Filename(nsAString& aString) { aString.Truncate(); }

    virtual ~Observation() {}

   protected:
    void Report();

    Operation mOperation;
    TimeStamp mStart;
    TimeStamp mEnd;
    const char* mReference;  // Identifies the source of the Observation
    bool mShouldReport;      // Measure and report if true
  };

  /**
   * Invoked whenever an implementation of the IOInterposeObserver should
   * observe aObservation. Implement this and do your thing...
   * But do consider if it is wise to use IO functions in this method, they are
   * likely to cause recursion :)
   * At least, see PoisonIOInterposer.h and register your handle as a debug file
   * even, if you don't initialize the poison IO interposer, someone else might.
   *
   * Remark: Observations may occur on any thread.
   */
  virtual void Observe(Observation& aObservation) = 0;

  virtual ~IOInterposeObserver() {}

 protected:
  /**
   * We don't use NS_IsMainThread() because we need to be able to determine the
   * main thread outside of XPCOM Initialization. IOInterposer observers should
   * call this function instead.
   */
  static bool IsMainThread();
};

/**
 * These functions are responsible for ensuring that events are routed to the
 * appropriate observers.
 */
namespace IOInterposer {

/**
 * This function must be called from the main-thread when no other threads are
 * running before any of the other methods on this class may be used.
 *
 * IO reports can however, safely assume that IsObservedOperation() will
 * return false until the IOInterposer is initialized.
 *
 * Remark, it's safe to call this method multiple times, so just call it when
 * you to utilize IO interposing.
 *
 * Using the IOInterposerInit class is preferred to calling this directly.
 */
bool Init();

/**
 * This function must be called from the main thread, and furthermore
 * it must be called when no other threads are executing. Effectively
 * restricting us to calling it only during shutdown.
 *
 * Callers should take care that no other consumers are subscribed to events,
 * as these events will stop when this function is called.
 *
 * In practice, we don't use this method as the IOInterposer is used for
 * late-write checks.
 */
void Clear();

/**
 * This function immediately disables IOInterposer functionality in a fast,
 * thread-safe manner. Primarily for use by the crash reporter.
 */
void Disable();

/**
 * This function re-enables IOInterposer functionality in a fast, thread-safe
 * manner.  Primarily for use by the crash reporter.
 */
void Enable();

/**
 * Report IO to registered observers.
 * Notice that the reported operation must be either OpRead, OpWrite or
 * OpFSync. You are not allowed to report an observation with OpWriteFSync or
 * OpAll, these are just auxiliary values for use with Register().
 *
 * If the IO call you're reporting does multiple things, write and fsync, you
 * can choose to call Report() twice once with write and once with FSync. You
 * may not call Report() with OpWriteFSync! The Observation::mOperation
 * attribute is meant to be generic, not perfect.
 *
 * Notice that there is no reason to report an observation with an operation
 * which is not being observed. Use IsObservedOperation() to check if the
 * operation you are about to report is being observed. This is especially
 * important if you are constructing expensive observations containing
 * filename and full-path.
 *
 * Remark: Init() must be called before any IO is reported. But
 * IsObservedOperation() will return false until Init() is called.
 */
void Report(IOInterposeObserver::Observation& aObservation);

/**
 * Return whether or not an operation is observed. Reporters should not
 * report operations that are not being observed by anybody. This mechanism
 * allows us to avoid reporting I/O when no observers are registered.
 */
bool IsObservedOperation(IOInterposeObserver::Operation aOp);

/**
 * Register IOInterposeObserver, the observer object will receive all
 * observations for the given operation aOp.
 *
 * Remark: Init() must be called before observers are registered.
 */
void Register(IOInterposeObserver::Operation aOp,
              IOInterposeObserver* aObserver);

/**
 * Unregister an IOInterposeObserver for a given operation
 * Remark: It is always safe to unregister for all operations, even if yoú
 * didn't register for them all.
 * I.e. IOInterposer::Unregister(IOInterposeObserver::OpAll, aObserver)
 *
 * Remark: Init() must be called before observers are unregistered.
 */
void Unregister(IOInterposeObserver::Operation aOp,
                IOInterposeObserver* aObserver);

/**
 * Registers the current thread with the IOInterposer. This must be done to
 * ensure that per-thread data is created in an orderly fashion.
 * We could have written this to initialize that data lazily, however this
 * could have unintended consequences if a thread that is not aware of
 * IOInterposer was implicitly registered: its per-thread data would never
 * be deleted because it would not know to unregister itself.
 *
 * @param aIsMainThread true if IOInterposer should treat the current thread
 *                      as the main thread.
 */
void RegisterCurrentThread(bool aIsMainThread = false);

/**
 * Unregisters the current thread with the IOInterposer. This is important
 * to call when a thread is shutting down because it cleans up data that
 * is stored in a TLS slot.
 */
void UnregisterCurrentThread();

/**
 * Called to inform observers that the process has transitioned out of the
 * startup stage or into the shutdown stage. Main thread only.
 */
void EnteringNextStage();

}  // namespace IOInterposer

class IOInterposerInit {
 public:
  IOInterposerInit() {
#if !defined(RELEASE_OR_BETA)
    IOInterposer::Init();
#endif
  }

  ~IOInterposerInit() {
#if !defined(RELEASE_OR_BETA)
    IOInterposer::Clear();
#endif
  }
};

class MOZ_RAII AutoIOInterposerDisable final {
 public:
  explicit AutoIOInterposerDisable(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    IOInterposer::Disable();
  }
  ~AutoIOInterposerDisable() { IOInterposer::Enable(); }

 private:
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};

}  // namespace mozilla

#endif  // mozilla_IOInterposer_h