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.

Header

Mercurial (e2394b695d21)

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
/* -*- 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/. */

#include "IOInterposer.h"
#include "NSPRInterposer.h"

#include "prio.h"
#include "private/pprio.h"
#include "nsDebug.h"
#include "nscore.h"

#include <sys/param.h>
#ifdef XP_MACOSX
#  include <fcntl.h>
#else
#  include "prprf.h"
#  include <unistd.h>
#endif

namespace {

/* Original IO methods */
PRCloseFN sCloseFn = nullptr;
PRReadFN sReadFn = nullptr;
PRWriteFN sWriteFn = nullptr;
PRFsyncFN sFSyncFn = nullptr;
PRFileInfoFN sFileInfoFn = nullptr;
PRFileInfo64FN sFileInfo64Fn = nullptr;

static int32_t GetPathFromFd(int32_t aFd, char* aBuf, size_t aBufSize) {
#ifdef XP_MACOSX
  NS_ASSERTION(aBufSize >= MAXPATHLEN,
               "aBufSize should be a least MAXPATHLEN long");

  return fcntl(aFd, F_GETPATH, aBuf);
#else
  char procPath[32];
  if (PR_snprintf(procPath, sizeof(procPath), "/proc/self/fd/%i", aFd) ==
      (PRUint32)-1) {
    return -1;
  }

  int32_t ret = readlink(procPath, aBuf, aBufSize - 1);
  if (ret > -1) {
    aBuf[ret] = '\0';
  }

  return ret;
#endif
}

/**
 * RAII class for timing the duration of an NSPR I/O call and reporting the
 * result to the mozilla::IOInterposeObserver API.
 */
class NSPRIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
 public:
  explicit NSPRIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
                                 PRFileDesc* aFd)
      : mozilla::IOInterposeObserver::Observation(aOp, "NSPRIOInterposer") {
    char filename[MAXPATHLEN];
    if (mShouldReport && aFd &&
        GetPathFromFd(PR_FileDesc2NativeHandle(aFd), filename,
                      sizeof(filename)) != -1) {
      mFilename = NS_ConvertUTF8toUTF16(filename);
    } else {
      mFilename.Truncate();
    }
  }

  void Filename(nsAString& aFilename) override { aFilename = mFilename; }

  ~NSPRIOAutoObservation() override { Report(); }

 private:
  nsString mFilename;
};

PRStatus PR_CALLBACK interposedClose(PRFileDesc* aFd) {
  // If we don't have a valid original function pointer something is very wrong.
  NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL");

  NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpClose, aFd);
  return sCloseFn(aFd);
}

int32_t PR_CALLBACK interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt) {
  // If we don't have a valid original function pointer something is very wrong.
  NS_ASSERTION(sReadFn, "NSPR IO Interposing: sReadFn is NULL");

  NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpRead, aFd);
  return sReadFn(aFd, aBuf, aAmt);
}

int32_t PR_CALLBACK interposedWrite(PRFileDesc* aFd, const void* aBuf,
                                    int32_t aAmt) {
  // If we don't have a valid original function pointer something is very wrong.
  NS_ASSERTION(sWriteFn, "NSPR IO Interposing: sWriteFn is NULL");

  NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpWrite, aFd);
  return sWriteFn(aFd, aBuf, aAmt);
}

PRStatus PR_CALLBACK interposedFSync(PRFileDesc* aFd) {
  // If we don't have a valid original function pointer something is very wrong.
  NS_ASSERTION(sFSyncFn, "NSPR IO Interposing: sFSyncFn is NULL");

  NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpFSync, aFd);
  return sFSyncFn(aFd);
}

PRStatus PR_CALLBACK interposedFileInfo(PRFileDesc* aFd, PRFileInfo* aInfo) {
  // If we don't have a valid original function pointer something is very wrong.
  NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL");

  NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpStat, aFd);
  return sFileInfoFn(aFd, aInfo);
}

PRStatus PR_CALLBACK interposedFileInfo64(PRFileDesc* aFd,
                                          PRFileInfo64* aInfo) {
  // If we don't have a valid original function pointer something is very wrong.
  NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL");

  NSPRIOAutoObservation timer(mozilla::IOInterposeObserver::OpStat, aFd);
  return sFileInfo64Fn(aFd, aInfo);
}

}  // namespace

namespace mozilla {

void InitNSPRIOInterposing() {
  // Check that we have not interposed any of the IO methods before
  MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn &&
             !sFileInfo64Fn);

  // We can't actually use this assertion because we initialize this code
  // before XPCOM is initialized, so NS_IsMainThread() always returns false.
  // MOZ_ASSERT(NS_IsMainThread());

  // Get IO methods from NSPR and const cast the structure so we can modify it.
  PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());

  // Something is badly wrong if we don't get IO methods... However, we don't
  // want to crash over that in non-debug builds. This is unlikely to happen
  // so an assert is enough, no need to report it to the caller.
  MOZ_ASSERT(methods);
  if (!methods) {
    return;
  }

  // Store original functions
  sCloseFn = methods->close;
  sReadFn = methods->read;
  sWriteFn = methods->write;
  sFSyncFn = methods->fsync;
  sFileInfoFn = methods->fileInfo;
  sFileInfo64Fn = methods->fileInfo64;

  // Overwrite with our interposed functions
  methods->close = &interposedClose;
  methods->read = &interposedRead;
  methods->write = &interposedWrite;
  methods->fsync = &interposedFSync;
  methods->fileInfo = &interposedFileInfo;
  methods->fileInfo64 = &interposedFileInfo64;
}

void ClearNSPRIOInterposing() {
  // If we have already cleared IO interposing, or not initialized it this is
  // actually bad.
  MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn &&
             sFileInfo64Fn);

  // Get IO methods from NSPR and const cast the structure so we can modify it.
  PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());

  // Something is badly wrong if we don't get IO methods... However, we don't
  // want to crash over that in non-debug builds. This is unlikely to happen
  // so an assert is enough, no need to report it to the caller.
  MOZ_ASSERT(methods);
  if (!methods) {
    return;
  }

  // Restore original functions
  methods->close = sCloseFn;
  methods->read = sReadFn;
  methods->write = sWriteFn;
  methods->fsync = sFSyncFn;
  methods->fileInfo = sFileInfoFn;
  methods->fileInfo64 = sFileInfo64Fn;

  // Forget about original functions
  sCloseFn = nullptr;
  sReadFn = nullptr;
  sWriteFn = nullptr;
  sFSyncFn = nullptr;
  sFileInfoFn = nullptr;
  sFileInfo64Fn = nullptr;
}

}  // namespace mozilla