Backed out changeset 2d6c5cbcc213 (bug 902587) for failures during linking on a CLOSED TREE
authorEd Morley <emorley@mozilla.com>
Fri, 25 Oct 2013 14:24:34 +0100
changeset 152514 429806586db9c3f2da318e91c7a6a7897b980a5c
parent 152513 b35cd52a1f40ef5a7441144a6c38c8bdf5265963
child 152515 bd9cbc876172602a2d3a0cae2f059d3ac66fd826
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
bugs902587
milestone27.0a1
backs out2d6c5cbcc2138976abce2b1431ad893c9c3f0f08
Backed out changeset 2d6c5cbcc213 (bug 902587) for failures during linking on a CLOSED TREE
browser/app/nsBrowserApp.cpp
media/mtransport/test/mtransport_test_utils.h
storage/src/mozStorageService.cpp
toolkit/components/startup/nsAppStartup.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/xre/nsAppRunner.cpp
tools/trace-malloc/lib/nsTraceMalloc.c
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsTraceRefcntImpl.cpp
xpcom/build/LateWriteChecks.cpp
xpcom/build/LateWriteChecks.h
xpcom/build/PoisonIOInterposer.h
xpcom/build/PoisonIOInterposerBase.cpp
xpcom/build/PoisonIOInterposerMac.cpp
xpcom/build/PoisonIOInterposerWin.cpp
xpcom/build/moz.build
xpcom/build/mozPoisonWrite.h
xpcom/build/mozPoisonWriteBase.cpp
xpcom/build/mozPoisonWriteBase.h
xpcom/build/mozPoisonWriteMac.cpp
xpcom/build/mozPoisonWriteStub.cpp
xpcom/build/mozPoisonWriteWin.cpp
xpcom/build/nsXPComInit.cpp
xpcom/build/nsXULAppAPI.h
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -145,29 +145,29 @@ XRE_GetFileFromPathType XRE_GetFileFromP
 XRE_CreateAppDataType XRE_CreateAppData;
 XRE_FreeAppDataType XRE_FreeAppData;
 #ifdef XRE_HAS_DLL_BLOCKLIST
 XRE_SetupDllBlocklistType XRE_SetupDllBlocklist;
 #endif
 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
 XRE_mainType XRE_main;
-XRE_StopLateWriteChecksType XRE_StopLateWriteChecks;
+XRE_DisableWritePoisoningType XRE_DisableWritePoisoning;
 
 static const nsDynamicFunctionLoad kXULFuncs[] = {
     { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
     { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
     { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
 #ifdef XRE_HAS_DLL_BLOCKLIST
     { "XRE_SetupDllBlocklist", (NSFuncPtr*) &XRE_SetupDllBlocklist },
 #endif
     { "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
     { "XRE_StartupTimelineRecord", (NSFuncPtr*) &XRE_StartupTimelineRecord },
     { "XRE_main", (NSFuncPtr*) &XRE_main },
-    { "XRE_StopLateWriteChecks", (NSFuncPtr*) &XRE_StopLateWriteChecks },
+    { "XRE_DisableWritePoisoning", (NSFuncPtr*) &XRE_DisableWritePoisoning },
     { nullptr, nullptr }
 };
 
 static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
 {
   nsCOMPtr<nsIFile> appini;
   nsresult rv;
   uint32_t mainFlags = 0;
@@ -637,13 +637,13 @@ int main(int argc, char* argv[])
   NS_LogTerm();
 
 #ifdef XP_MACOSX
   // Allow writes again. While we would like to catch writes from static
   // destructors to allow early exits to use _exit, we know that there is
   // at least one such write that we don't control (see bug 826029). For
   // now we enable writes again and early exits will have to use exit instead
   // of _exit.
-  XRE_StopLateWriteChecks();
+  XRE_DisableWritePoisoning();
 #endif
 
   return result;
 }
--- a/media/mtransport/test/mtransport_test_utils.h
+++ b/media/mtransport/test/mtransport_test_utils.h
@@ -24,16 +24,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #ifdef MOZ_CRASHREPORTER
 #include "nsICrashReporter.h"
 #endif
 #include "nsPISocketTransportService.h"
 #include "nsServiceManagerUtils.h"
 #include "TestHarness.h"
+#include "mozilla/mozPoisonWrite.h"
 
 class MtransportTestUtils {
  public:
   MtransportTestUtils() : xpcom_("") {
     if (!sts_) {
       InitServices();
     }
   }
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -17,17 +17,17 @@
 #include "mozStoragePrivateHelpers.h"
 #include "nsILocale.h"
 #include "nsILocaleService.h"
 #include "nsIXPConnect.h"
 #include "nsIObserverService.h"
 #include "nsIPropertyBag2.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/LateWriteChecks.h"
+#include "mozilla/mozPoisonWrite.h"
 #include "mozIStorageCompletionCallback.h"
 
 #include "sqlite3.h"
 
 #ifdef SQLITE_OS_WIN
 // "windows.h" was included and it can #define lots of things we care about...
 #undef CompareString
 #endif
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -32,16 +32,17 @@
 #include "nsWidgetsCID.h"
 #include "nsAppShellCID.h"
 #include "nsXPCOMCIDInternal.h"
 #include "mozilla/Services.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "prenv.h"
 #include "nsAppDirectoryServiceDefs.h"
+#include "mozilla/mozPoisonWrite.h"
 
 #if defined(XP_WIN)
 // Prevent collisions with nsAppStartup::GetStartupInfo()
 #undef GetStartupInfo
 #endif
 
 #include "mozilla/Telemetry.h"
 #include "mozilla/StartupTimeline.h"
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -44,17 +44,17 @@
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "mozilla/ProcessedStack.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/mozPoisonWrite.h"
 #if defined(MOZ_ENABLE_PROFILER_SPS)
 #include "shared-libraries.h"
 #endif
 
 namespace {
 
 using namespace base;
 using namespace mozilla;
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -77,17 +77,17 @@ using mozilla::InjectCrashRunnable;
 #include "nsDebug.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "prprf.h"
 #include <map>
 #include <vector>
 
 #include "mozilla/mozalloc_oom.h"
-#include "mozilla/LateWriteChecks.h"
+#include "mozilla/mozPoisonWrite.h"
 
 #if defined(XP_MACOSX)
 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
 #endif
 #if defined(MOZ_WIDGET_ANDROID)
 #include "common/linux/file_id.h"
 #endif
 
@@ -787,17 +787,17 @@ static bool ShouldReport()
     return false;
   }
 
   return true;
 }
 
 namespace {
   bool Filter(void* context) {
-    mozilla::StopLateWriteChecks();
+    mozilla::DisableWritePoisoning();
     return true;
   }
 }
 
 
 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
                              bool force/*=false*/)
 {
@@ -2229,17 +2229,17 @@ OnChildProcessDumpRequested(void* aConte
 static bool
 OOPInitialized()
 {
   return pidToMinidump != nullptr;
 }
 
 #ifdef XP_MACOSX
 static bool ChildFilter(void *context) {
-  mozilla::StopLateWriteChecks();
+  mozilla::DisableWritePoisoning();
   return true;
 }
 #endif
 
 void
 OOPInit()
 {
   if (OOPInitialized())
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -124,17 +124,17 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsXULAppAPI.h"
 #include "nsXREDirProvider.h"
 #include "nsToolkitCompsCID.h"
 
 #include "nsINIParser.h"
 #include "mozilla/Omnijar.h"
 #include "mozilla/StartupTimeline.h"
-#include "mozilla/LateWriteChecks.h"
+#include "mozilla/mozPoisonWrite.h"
 
 #include <stdlib.h>
 
 // for old system jemalloc version check
 #if !defined(MOZ_MEMORY) && defined(__NetBSD__)
 #include <sys/param.h>
 #endif
 
@@ -3956,20 +3956,20 @@ XREMain::XRE_main(int argc, char* argv[]
 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
   mozilla::ShutdownEventTracing();
 #endif
 
   // Check for an application initiated restart.  This is one that
   // corresponds to nsIAppStartup.quit(eRestart)
   if (rv == NS_SUCCESS_RESTART_APP) {
     appInitiatedRestart = true;
-
-    // We have an application restart don't do any shutdown checks here
-    // In particular we don't want to poison IO for checking late-writes.
-    gShutdownChecks = SCM_NOTHING;
+  } else {
+    // We will have a real shutdown, let ShutdownXPCOM poison writes to
+    // find any late ones.
+    mozilla::EnableWritePoisoning();
   }
 
   if (!mShuttingDown) {
 #ifdef MOZ_ENABLE_XREMOTE
     // shut down the x remote proxy window
     if (mRemoteService) {
       mRemoteService->Shutdown();
     }
@@ -4139,18 +4139,18 @@ XRE_mainMetro(int argc, char* argv[], co
                "XPCOM Shutdown hasn't occured, and we are exiting.");
   return 0;
 }
 
 void SetWindowsEnvironment(WindowsEnvironmentType aEnvID);
 #endif // MOZ_METRO || !defined(XP_WIN)
 
 void
-XRE_StopLateWriteChecks(void) {
-  mozilla::StopLateWriteChecks();
+XRE_DisableWritePoisoning(void) {
+  mozilla::DisableWritePoisoning();
 }
 
 int
 XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags)
 {
 #if !defined(MOZ_METRO) || !defined(XP_WIN)
   XREMain main;
   int result = main.XRE_main(argc, argv, aAppData);
--- a/tools/trace-malloc/lib/nsTraceMalloc.c
+++ b/tools/trace-malloc/lib/nsTraceMalloc.c
@@ -30,17 +30,17 @@
 #include "nsTraceMalloc.h"
 #include "nscore.h"
 #include "prinit.h"
 #include "prthread.h"
 #include "plstr.h"
 #include "nsStackWalk.h"
 #include "nsTraceMallocCallbacks.h"
 #include "nsTypeInfo.h"
-#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/mozPoisonWrite.h"
 
 #if defined(XP_MACOSX)
 
 #include <malloc/malloc.h>
 
 #define WRITE_FLAGS "w"
 
 #define __libc_malloc(x)                malloc(x)
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -118,17 +118,17 @@
 #include "nsIFile.h"
 #include "nsMemoryInfoDumper.h"
 #include "xpcpublic.h"
 #include "GeckoProfiler.h"
 #include <stdint.h>
 #include <stdio.h>
 
 #include "mozilla/Likely.h"
-#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/mozPoisonWrite.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThreadLocal.h"
 
 using namespace mozilla;
 
 //#define COLLECT_TIME_DEBUG
 
 // Enable assertions that are useful for diagnosing errors in graph construction.
--- a/xpcom/base/nsTraceRefcntImpl.cpp
+++ b/xpcom/base/nsTraceRefcntImpl.cpp
@@ -26,17 +26,17 @@
 #include <unistd.h>
 #endif
 
 #ifdef NS_TRACE_MALLOC
 #include "nsTraceMalloc.h"
 #endif
 
 #include "mozilla/BlockingResourceBase.h"
-#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/mozPoisonWrite.h"
 
 #ifdef HAVE_DLOPEN
 #include <dlfcn.h>
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void
deleted file mode 100644
--- a/xpcom/build/LateWriteChecks.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim:set ts=4 sw=4 sts=4 ci et: */
-/* 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 <algorithm>
-
-#include "mozilla/IOInterposer.h"
-#include "mozilla/PoisonIOInterposer.h"
-#include "mozilla/ProcessedStack.h"
-#include "mozilla/SHA1.h"
-#include "mozilla/Scoped.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/Telemetry.h"
-#include "nsAppDirectoryServiceDefs.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsPrintfCString.h"
-#include "nsStackWalk.h"
-#include "plstr.h"
-
-#ifdef XP_WIN
-#define NS_T(str) L ## str
-#define NS_SLASH "\\"
-#include <fcntl.h>
-#include <io.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <windows.h>
-#else
-#define NS_SLASH "/"
-#endif
-
-#include "LateWriteChecks.h"
-
-using namespace mozilla;
-
-/*************************** Auxiliary Declarations ***************************/
-
-// This a wrapper over a file descriptor that provides a Printf method and
-// computes the sha1 of the data that passes through it.
-class SHA1Stream
-{
-public:
-    explicit SHA1Stream(FILE *stream)
-      : mFile(stream)
-    {
-      MozillaRegisterDebugFILE(mFile);
-    }
-
-    void Printf(const char *aFormat, ...)
-    {
-        MOZ_ASSERT(mFile);
-        va_list list;
-        va_start(list, aFormat);
-        nsAutoCString str;
-        str.AppendPrintf(aFormat, list);
-        va_end(list);
-        mSHA1.update(str.get(), str.Length());
-        fwrite(str.get(), 1, str.Length(), mFile);
-    }
-    void Finish(SHA1Sum::Hash &aHash)
-    {
-        int fd = fileno(mFile);
-        fflush(mFile);
-        MozillaUnRegisterDebugFD(fd);
-        fclose(mFile);
-        mSHA1.finish(aHash);
-        mFile = NULL;
-    }
-private:
-    FILE *mFile;
-    SHA1Sum mSHA1;
-};
-
-static void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
-{
-    std::vector<uintptr_t> *stack =
-        static_cast<std::vector<uintptr_t>*>(aClosure);
-    stack->push_back(reinterpret_cast<uintptr_t>(aPC));
-}
-
-/**************************** Late-Write Observer  ****************************/
-
-/**
- * An implementation of IOInterposeObserver to be registered with IOInterposer.
- * This observer logs all writes as late writes.
- */
-class LateWriteObserver MOZ_FINAL : public IOInterposeObserver
-{
-public:
-  LateWriteObserver(const char* aProfileDirectory)
-    : mProfileDirectory(PL_strdup(aProfileDirectory))
-  {
-  }
-  ~LateWriteObserver() {
-    PL_strfree(mProfileDirectory);
-    mProfileDirectory = nullptr;
-  }
-
-  void Observe(IOInterposeObserver::Observation& aObservation);
-private:
-  char* mProfileDirectory;
-};
-
-void LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
-{
-  // Crash if that is the shutdown check mode
-  if (gShutdownChecks == SCM_CRASH) {
-    MOZ_CRASH();
-  }
-
-  // If we have shutdown mode SCM_NOTHING or we can't record then abort
-  if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecord()) {
-    return;
-  }
-
-  // Write the stack and loaded libraries to a file. We can get here
-  // concurrently from many writes, so we use multiple temporary files.
-  std::vector<uintptr_t> rawStack;
-
-  NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
-               reinterpret_cast<void*>(&rawStack), 0, nullptr);
-  Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
-
-  nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
-                          NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
-  char *name;
-  nameAux.GetMutableData(&name);
-
-  // We want the sha1 of the entire file, so please don't write to fd
-  // directly; use sha1Stream.
-  FILE *stream;
-#ifdef XP_WIN
-  HANDLE hFile;
-  do {
-    // mkstemp isn't supported so keep trying until we get a file
-    int result = _mktemp_s(name, strlen(name) + 1);
-    hFile = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_NEW,
-                        FILE_ATTRIBUTE_NORMAL, NULL);
-  } while (GetLastError() == ERROR_FILE_EXISTS);
-
-  if (hFile == INVALID_HANDLE_VALUE) {
-    NS_RUNTIMEABORT("Um, how did we get here?");
-  }
-
-  // http://support.microsoft.com/kb/139640
-  int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
-  if (fd == -1) {
-    NS_RUNTIMEABORT("Um, how did we get here?");
-  }
-
-  stream = _fdopen(fd, "w");
-#else
-  int fd = mkstemp(name);
-  stream = fdopen(fd, "w");
-#endif
-
-  SHA1Stream sha1Stream(stream);
-
-  size_t numModules = stack.GetNumModules();
-  sha1Stream.Printf("%u\n", (unsigned)numModules);
-  for (size_t i = 0; i < numModules; ++i) {
-    Telemetry::ProcessedStack::Module module = stack.GetModule(i);
-    sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
-                      module.mName.c_str());
-  }
-
-  size_t numFrames = stack.GetStackSize();
-  sha1Stream.Printf("%u\n", (unsigned)numFrames);
-  for (size_t i = 0; i < numFrames; ++i) {
-    const Telemetry::ProcessedStack::Frame &frame =
-        stack.GetFrame(i);
-    // NOTE: We write the offsets, while the atos tool expects a value with
-    // the virtual address added. For example, running otool -l on the the firefox
-    // binary shows
-    //      cmd LC_SEGMENT_64
-    //      cmdsize 632
-    //      segname __TEXT
-    //      vmaddr 0x0000000100000000
-    // so to print the line matching the offset 123 one has to run
-    // atos -o firefox 0x100000123.
-    sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
-  }
-
-  SHA1Sum::Hash sha1;
-  sha1Stream.Finish(sha1);
-
-  // Note: These files should be deleted by telemetry once it reads them. If
-  // there were no telemetry runs by the time we shut down, we just add files
-  // to the existing ones instead of replacing them. Given that each of these
-  // files is a bug to be fixed, that is probably the right thing to do.
-
-  // We append the sha1 of the contents to the file name. This provides a simple
-  // client side deduplication.
-  nsPrintfCString finalName("%s%s", mProfileDirectory,
-                            "/Telemetry.LateWriteFinal-");
-  for (int i = 0; i < 20; ++i) {
-    finalName.AppendPrintf("%02x", sha1[i]);
-  }
-  PR_Delete(finalName.get());
-  PR_Rename(name, finalName.get());
-}
-
-/******************************* Setup/Teardown *******************************/
-
-static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
-
-namespace mozilla{
-
-void InitLateWriteChecks()
-{
-  nsCOMPtr<nsIFile> mozFile;
-  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
-  if (mozFile) {
-    nsAutoCString nativePath;
-    nsresult rv = mozFile->GetNativePath(nativePath);
-    if (NS_SUCCEEDED(rv) && nativePath.get()) {
-      sLateWriteObserver = new LateWriteObserver(nativePath.get());
-    }
-  }
-}
-
-void BeginLateWriteChecks()
-{
-  if (sLateWriteObserver) {
-    IOInterposer::Register(
-      IOInterposeObserver::OpWriteFSync,
-      sLateWriteObserver
-    );
-  }
-}
-
-void StopLateWriteChecks()
-{
-  if (sLateWriteObserver) {
-    IOInterposer::Unregister(
-      IOInterposeObserver::OpAll,
-      sLateWriteObserver
-    );
-    // Deallocation would not be thread-safe, and StopLateWriteChecks() is
-    // called at shutdown and only in special cases.
-    // sLateWriteObserver = nullptr;
-  }
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/xpcom/build/LateWriteChecks.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim:set ts=4 sw=4 sts=4 ci et: */
-/* 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_LateWriteChecks_h
-#define mozilla_LateWriteChecks_h
-
-// This file, along with LateWriteChecks.cpp, serves to check for and report
-// late writes. The idea is discover writes to the file system that happens
-// during shutdown such that these maybe be moved forward and the process may be
-// killed without waiting for static destructors.
-
-namespace mozilla {
-
-/** Different shutdown check modes */
-enum ShutdownChecksMode {
-  SCM_CRASH,      /** Crash on shutdown check failure */
-  SCM_RECORD,     /** Record shutdown check violations */
-  SCM_NOTHING     /** Don't attempt any shutdown checks */
-};
-
-/**
- * Current shutdown check mode.
- * This variable is defined and initialized in nsAppRunner.cpp
- */
-extern ShutdownChecksMode gShutdownChecks;
-
-/**
- * Allocate structures and acquire information from XPCOM necessary to do late
- * write checks. This function must be invoked before BeginLateWriteChecks()
- * and before XPCOM has stopped working.
- */
-void InitLateWriteChecks();
-
-/**
- * Begin recording all writes as late-writes. This function should be called
- * when all legitimate writes have occurred. This function does not rely on
- * XPCOM as it is designed to be invoked during XPCOM shutdown.
- *
- * For late-write checks to work you must initialize one or more backends that
- * reports IO through the IOInterposer API. PoisonIOInterposer would probably
- * be the backend of choice in this case.
- *
- * Note: BeginLateWriteChecks() must have been invoked before this function.
- */
-void BeginLateWriteChecks();
-
-/**
- * Stop recording all writes as late-writes, call this function when you want
- * late-write checks to stop. I.e. exception handling, or the special case on
- * Mac described in bug 826029.
- */
-void StopLateWriteChecks();
-
-} // mozilla
-
-#endif // mozilla_LateWriteChecks_h
--- a/xpcom/build/moz.build
+++ b/xpcom/build/moz.build
@@ -12,53 +12,54 @@ EXPORTS += [
     'nsXPCOMCIDInternal.h',
     'nsXREAppData.h',
     'nsXULAppAPI.h',
     'xrecore.h',
 ]
 
 EXPORTS.mozilla += [
     'FileLocation.h',
-    'LateWriteChecks.h',
+    'mozPoisonWrite.h',
     'Omnijar.h',
-    'PoisonIOInterposer.h',
     'ServiceList.h',
     'Services.h',
     'XPCOM.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla += ['perfprobe.h']
+    SOURCES += [
+        'mozPoisonWriteBase.cpp',
+        'mozPoisonWriteWin.cpp',
+        'perfprobe.cpp',
+    ]
+elif CONFIG['OS_ARCH'] == 'Darwin':
+    SOURCES += [
+        'mozPoisonWriteBase.cpp',
+        'mozPoisonWriteMac.cpp',
+    ]
+else:
+    SOURCES += [
+        'mozPoisonWriteStub.cpp',
+    ]
 
 include('../glue/objs.mozbuild')
 
 SOURCES += xpcom_gluens_src_cppsrcs
 SOURCES += xpcom_glue_src_cppsrcs
 
 SOURCES += [
     'FileLocation.cpp',
     'FrozenFunctions.cpp',
-    'LateWriteChecks.cpp',
     'nsXPComInit.cpp',
     'nsXPCOMStrings.cpp',
     'Omnijar.cpp',
     'Services.cpp',
 ]
 
-if CONFIG['OS_TARGET'] == 'Darwin':
-    SOURCES += [
-        'PoisonIOInterposerBase.cpp',
-        'PoisonIOInterposerMac.cpp',
-    ]
-elif CONFIG['OS_TARGET'] == 'WINNT':
-    SOURCES += [
-        'PoisonIOInterposerBase.cpp',
-        'PoisonIOInterposerWin.cpp',
-    ]
-
 LIBXUL_LIBRARY = True
 
 MSVC_ENABLE_PGO = True
 
 LIBRARY_NAME = 'xpcom_core'
 
 EXPORT_LIBRARY = True
 
rename from xpcom/build/PoisonIOInterposer.h
rename to xpcom/build/mozPoisonWrite.h
--- a/xpcom/build/PoisonIOInterposer.h
+++ b/xpcom/build/mozPoisonWrite.h
@@ -1,88 +1,36 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim:set ts=4 sw=4 sts=4 ci et: */
 /* 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_PoisonIOInterposer_h
-#define mozilla_PoisonIOInterposer_h
+#ifndef MOZPOISONWRITE_H
+#define MOZPOISONWRITE_H
 
 #include "mozilla/Types.h"
 #include <stdio.h>
 
-#if defined(MOZ_ENABLE_PROFILER_SPS) && (defined(XP_WIN) || defined(XP_MACOSX))
-
 MOZ_BEGIN_EXTERN_C
-
-/** Register file descriptor to be ignored by poisoning IO interposer */
-void MozillaRegisterDebugFD(int fd);
-
-/** Register file to be ignored by poisoning IO interposer */
-void MozillaRegisterDebugFILE(FILE *f);
-
-/** Unregister file descriptor from being ignored by poisoning IO interposer */
-void MozillaUnRegisterDebugFD(int fd);
-
-/** Unregister file from being ignored by poisoning IO interposer */
-void MozillaUnRegisterDebugFILE(FILE *f);
-
+  void MozillaRegisterDebugFD(int fd);
+  void MozillaRegisterDebugFILE(FILE *f);
+  void MozillaUnRegisterDebugFD(int fd);
+  void MozillaUnRegisterDebugFILE(FILE *f);
 MOZ_END_EXTERN_C
 
 #ifdef __cplusplus
 namespace mozilla {
-
-/**
- * Check if a file is registered as a debug file.
- */
-bool IsDebugFile(intptr_t aFileID);
-
-/**
- * Initialize IO poisoning, this is only safe to do on the main-thread when no
- * other threads are running.
- *
- * Please, note that this probably has performance implications as all
- */
-void InitPoisonIOInterposer();
-
-#ifdef XP_MACOSX
-/**
- * Check that writes are dirty before reporting I/O (Mac OS X only)
- * This is necessary for late-write checks on Mac OS X, but reading the buffer
- * from file to see if we're writing dirty bits is expensive, so we don't want
- * to do this for everything else that uses
- */
-void OnlyReportDirtyWrites();
-#endif /* XP_MACOSX */
+enum ShutdownChecksMode {
+  SCM_CRASH,
+  SCM_RECORD,
+  SCM_NOTHING
+};
+extern ShutdownChecksMode gShutdownChecks;
 
-/**
- * Clear IO poisoning, this is only safe to do on the main-thread when no other
- * threads are running.
- */
-void ClearPoisonIOInterposer();
-
-} // namespace mozilla
-#endif /* __cplusplus */
-
-#else /* MOZ_ENABLE_PROFILER_SPS && (XP_WIN || XP_MACOSX) */
+void InitWritePoisoning();
+void PoisonWrite();
+void DisableWritePoisoning();
+void EnableWritePoisoning();
+}
+#endif
 
-MOZ_BEGIN_EXTERN_C
-inline void MozillaRegisterDebugFD(int fd){}
-inline void MozillaRegisterDebugFILE(FILE *f){}
-inline void MozillaUnRegisterDebugFD(int fd){}
-inline void MozillaUnRegisterDebugFILE(FILE *f){}
-MOZ_END_EXTERN_C
-
-#ifdef __cplusplus
-namespace mozilla {
-inline bool IsDebugFile(intptr_t aFileID){ return true; }
-inline void InitPoisonIOInterposer(){}
-inline void ClearPoisonIOInterposer(){}
-#ifdef XP_MACOSX
-inline void OnlyReportDirtyWrites(){}
-#endif /* XP_MACOSX */
-} // namespace mozilla
-#endif /* __cplusplus */
-
-#endif /* MOZ_ENABLE_PROFILER_SPS && (XP_WIN || XP_MACOSX) */
-
-#endif // mozilla_PoisonIOInterposer_h
+#endif
rename from xpcom/build/PoisonIOInterposerBase.cpp
rename to xpcom/build/mozPoisonWriteBase.cpp
--- a/xpcom/build/PoisonIOInterposerBase.cpp
+++ b/xpcom/build/mozPoisonWriteBase.cpp
@@ -1,34 +1,38 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 ci et: */
 /* 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 "mozilla/Mutex.h"
+#include "mozPoisonWrite.h"
+#include "mozPoisonWriteBase.h"
+#include "mozilla/ProcessedStack.h"
 #include "mozilla/Scoped.h"
-
+#include "mozilla/SHA1.h"
+#include "mozilla/Telemetry.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsStackWalk.h"
+#include "nsPrintfCString.h"
+#include "plstr.h"
 #include <algorithm>
-#include <vector>
-
-#include "PoisonIOInterposer.h"
-
-// Auxiliary method to convert file descriptors to ids
-#if defined(XP_WIN32)
+#ifdef XP_WIN
+#define NS_T(str) L ## str
+#define NS_SLASH "\\"
+#include <windows.h>
 #include <io.h>
-inline intptr_t FileDescriptorToID(int aFd) {
-  return _get_osfhandle(aFd);
-}
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
 #else
-inline intptr_t FileDescriptorToID(int aFd) {
-  return aFd;
-}
-#endif /* if not XP_WIN32 */
-
+#define NS_SLASH "/"
+#endif
 using namespace mozilla;
 
 namespace {
 struct DebugFilesAutoLockTraits {
   typedef PRLock *type;
   const static type empty() {
     return nullptr;
   }
@@ -43,18 +47,20 @@ public:
   static void Clear();
   static PRLock *getDebugFileIDsLock() {
     // On windows this static is not thread safe, but we know that the first
     // call is from
     // * An early registration of a debug FD or
     // * The call to InitWritePoisoning.
     // Since the early debug FDs are logs created early in the main thread
     // and no writes are trapped before InitWritePoisoning, we are safe.
-    if (!Lock) {
+    static bool Initialized = false;
+    if (!Initialized) {
       Lock = PR_NewLock();
+      Initialized = true;
     }
 
     // We have to use something lower level than a mutex. If we don't, we
     // can get recursive in here when called from logging a call to free.
     return Lock;
   }
 
   DebugFilesAutoLock() :
@@ -64,87 +70,274 @@ public:
 };
 
 PRLock *DebugFilesAutoLock::Lock;
 void DebugFilesAutoLock::Clear() {
   MOZ_ASSERT(Lock != nullptr);
   Lock = nullptr;
 }
 
+static char *sProfileDirectory = nullptr;
+
 // Return a vector used to hold the IDs of the current debug files. On unix
 // an ID is a file descriptor. On Windows it is a file HANDLE.
 std::vector<intptr_t>* getDebugFileIDs() {
-  PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(DebugFilesAutoLock::getDebugFileIDsLock());
+  PRLock *lock = DebugFilesAutoLock::getDebugFileIDsLock();
+  PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(lock);
   // We have to use new as some write happen during static destructors
   // so an static std::vector might be destroyed while we still need it.
   static std::vector<intptr_t> *DebugFileIDs = new std::vector<intptr_t>();
   return DebugFileIDs;
 }
 
-} // anonymous namespace
+// This a wrapper over a file descriptor that provides a Printf method and
+// computes the sha1 of the data that passes through it.
+class SHA1Stream
+{
+public:
+    explicit SHA1Stream(FILE *stream)
+      : mFile(stream)
+    {
+      MozillaRegisterDebugFILE(mFile);
+    }
+
+    void Printf(const char *aFormat, ...)
+    {
+        MOZ_ASSERT(mFile);
+        va_list list;
+        va_start(list, aFormat);
+        nsAutoCString str;
+        str.AppendPrintf(aFormat, list);
+        va_end(list);
+        mSHA1.update(str.get(), str.Length());
+        fwrite(str.get(), 1, str.Length(), mFile);
+    }
+    void Finish(SHA1Sum::Hash &aHash)
+    {
+        int fd = fileno(mFile);
+        fflush(mFile);
+        MozillaUnRegisterDebugFD(fd);
+        fclose(mFile);
+        mSHA1.finish(aHash);
+        mFile = nullptr;
+    }
+private:
+    FILE *mFile;
+    SHA1Sum mSHA1;
+};
+
+static void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
+{
+    std::vector<uintptr_t> *stack =
+        static_cast<std::vector<uintptr_t>*>(aClosure);
+    stack->push_back(reinterpret_cast<uintptr_t>(aPC));
+}
+
+
+enum PoisonState {
+  POISON_UNINITIALIZED = 0,
+  POISON_ON,
+  POISON_OFF
+};
+
+// POISON_OFF has two consequences
+// * It prevents PoisonWrite from patching the write functions.
+// * If the patching has already been done, it prevents AbortOnBadWrite from
+//   asserting. Note that not all writes use AbortOnBadWrite at this point
+//   (aio_write for example), so disabling writes after patching doesn't
+//   completely undo it.
+PoisonState sPoisoningState = POISON_UNINITIALIZED;
+}
+
+namespace mozilla {
+
+void InitWritePoisoning()
+{
+  // Stdout and Stderr are OK.
+  MozillaRegisterDebugFD(1);
+  MozillaRegisterDebugFD(2);
+
+  nsCOMPtr<nsIFile> mozFile;
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
+  if (mozFile) {
+    nsAutoCString nativePath;
+    nsresult rv = mozFile->GetNativePath(nativePath);
+    if (NS_SUCCEEDED(rv)) {
+      sProfileDirectory = PL_strdup(nativePath.get());
+    }
+  }
+}
+
+bool ValidWriteAssert(bool ok)
+{
+    if (gShutdownChecks == SCM_CRASH && !ok) {
+        MOZ_CRASH();
+    }
+
+    // We normally don't poison writes if gShutdownChecks is SCM_NOTHING, but
+    // write poisoning can get more users in the future (profiling for example),
+    // so make sure we behave correctly.
+    if (gShutdownChecks == SCM_NOTHING || ok || !sProfileDirectory ||
+        !Telemetry::CanRecord()) {
+        return ok;
+    }
+
+    // Write the stack and loaded libraries to a file. We can get here
+    // concurrently from many writes, so we use multiple temporary files.
+    std::vector<uintptr_t> rawStack;
+
+    NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+                 reinterpret_cast<void*>(&rawStack), 0, nullptr);
+    Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
 
-namespace mozilla{
+    nsPrintfCString nameAux("%s%s%s", sProfileDirectory,
+                            NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
+    char *name;
+    nameAux.GetMutableData(&name);
+
+    // We want the sha1 of the entire file, so please don't write to fd
+    // directly; use sha1Stream.
+    FILE *stream;
+#ifdef XP_WIN
+    HANDLE hFile;
+    do {
+      // mkstemp isn't supported so keep trying until we get a file
+      int result = _mktemp_s(name, strlen(name) + 1);
+      hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr,
+                          CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
+    } while (GetLastError() == ERROR_FILE_EXISTS);
+
+    if (hFile == INVALID_HANDLE_VALUE) {
+      NS_RUNTIMEABORT("Um, how did we get here?");
+    }
+
+    // http://support.microsoft.com/kb/139640
+    int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
+    if (fd == -1) {
+      NS_RUNTIMEABORT("Um, how did we get here?");
+    }
+
+    stream = _fdopen(fd, "w");
+#else
+    int fd = mkstemp(name);
+    stream = fdopen(fd, "w");
+#endif
+
+    SHA1Stream sha1Stream(stream);
+
+    size_t numModules = stack.GetNumModules();
+    sha1Stream.Printf("%u\n", (unsigned)numModules);
+    for (size_t i = 0; i < numModules; ++i) {
+        Telemetry::ProcessedStack::Module module = stack.GetModule(i);
+        sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
+                          module.mName.c_str());
+    }
 
-// Auxiliary Method to test if a file descriptor is registered to be ignored
-// by the poisoning IO interposer
+    size_t numFrames = stack.GetStackSize();
+    sha1Stream.Printf("%u\n", (unsigned)numFrames);
+    for (size_t i = 0; i < numFrames; ++i) {
+        const Telemetry::ProcessedStack::Frame &frame =
+            stack.GetFrame(i);
+        // NOTE: We write the offsets, while the atos tool expects a value with
+        // the virtual address added. For example, running otool -l on the the firefox
+        // binary shows
+        //      cmd LC_SEGMENT_64
+        //      cmdsize 632
+        //      segname __TEXT
+        //      vmaddr 0x0000000100000000
+        // so to print the line matching the offset 123 one has to run
+        // atos -o firefox 0x100000123.
+        sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
+    }
+
+    SHA1Sum::Hash sha1;
+    sha1Stream.Finish(sha1);
+
+    // Note: These files should be deleted by telemetry once it reads them. If
+    // there were no telemery runs by the time we shut down, we just add files
+    // to the existing ones instead of replacing them. Given that each of these
+    // files is a bug to be fixed, that is probably the right thing to do.
+
+    // We append the sha1 of the contents to the file name. This provides a simple
+    // client side deduplication.
+    nsPrintfCString finalName("%s%s", sProfileDirectory, "/Telemetry.LateWriteFinal-");
+    for (int i = 0; i < 20; ++i) {
+        finalName.AppendPrintf("%02x", sha1[i]);
+    }
+    PR_Delete(finalName.get());
+    PR_Rename(name, finalName.get());
+    return false;
+}
+
+void DisableWritePoisoning() {
+  if (sPoisoningState != POISON_ON)
+    return;
+
+  sPoisoningState = POISON_OFF;
+  PL_strfree(sProfileDirectory);
+  sProfileDirectory = nullptr;
+
+  PRLock *Lock;
+  {
+    DebugFilesAutoLock lockedScope;
+    delete getDebugFileIDs();
+    Lock = DebugFilesAutoLock::getDebugFileIDsLock();
+    DebugFilesAutoLock::Clear();
+  }
+  PR_DestroyLock(Lock);
+}
+void EnableWritePoisoning() {
+  sPoisoningState = POISON_ON;
+}
+
+bool PoisonWriteEnabled()
+{
+  return sPoisoningState == POISON_ON;
+}
+
 bool IsDebugFile(intptr_t aFileID) {
   DebugFilesAutoLock lockedScope;
 
   std::vector<intptr_t> &Vec = *getDebugFileIDs();
   return std::find(Vec.begin(), Vec.end(), aFileID) != Vec.end();
 }
 
-// Clean-up for the registered debug files.
-// We should probably make sure all debug files are unregistered instead.
-// But as the poison IO interposer is used for late-write checks we're not
-// disabling it at any point yet. So Really no need for this.
-//
-// void ClearDebugFileRegister() {
-//   PRLock *Lock;
-//   {
-//     DebugFilesAutoLock lockedScope;
-//     delete getDebugFileIDs();
-//     Lock = DebugFilesAutoLock::getDebugFileIDsLock();
-//     DebugFilesAutoLock::Clear();
-//   }
-//   PR_DestroyLock(Lock);
-// }
-
-} // namespace mozilla
+} // mozilla
 
 extern "C" {
-
   void MozillaRegisterDebugFD(int fd) {
+    if (sPoisoningState == POISON_OFF)
+      return;
+    DebugFilesAutoLock lockedScope;
     intptr_t fileId = FileDescriptorToID(fd);
-    DebugFilesAutoLock lockedScope;
     std::vector<intptr_t> &Vec = *getDebugFileIDs();
     MOZ_ASSERT(std::find(Vec.begin(), Vec.end(), fileId) == Vec.end());
     Vec.push_back(fileId);
   }
-
   void MozillaRegisterDebugFILE(FILE *f) {
+    if (sPoisoningState == POISON_OFF)
+      return;
     int fd = fileno(f);
-    if (fd == 1 || fd == 2) {
+    if (fd == 1 || fd == 2)
       return;
-    }
     MozillaRegisterDebugFD(fd);
   }
-
   void MozillaUnRegisterDebugFD(int fd) {
+    if (sPoisoningState == POISON_OFF)
+      return;
     DebugFilesAutoLock lockedScope;
     intptr_t fileId = FileDescriptorToID(fd);
     std::vector<intptr_t> &Vec = *getDebugFileIDs();
     std::vector<intptr_t>::iterator i =
       std::find(Vec.begin(), Vec.end(), fileId);
     MOZ_ASSERT(i != Vec.end());
     Vec.erase(i);
   }
-
   void MozillaUnRegisterDebugFILE(FILE *f) {
+    if (sPoisoningState == POISON_OFF)
+      return;
     int fd = fileno(f);
-    if (fd == 1 || fd == 2) {
+    if (fd == 1 || fd == 2)
       return;
-    }
     fflush(f);
     MozillaUnRegisterDebugFD(fd);
   }
-
-}
\ No newline at end of file
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/build/mozPoisonWriteBase.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 ci et: */
+/* 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/. */
+
+// Private interface for code shared between the platforms of mozPoisonWriteXXX
+
+#ifndef MOZPOISONWRITEBASE_H
+#define MOZPOISONWRITEBASE_H
+
+#include <stdio.h>
+#include <vector>
+#include "nspr.h"
+#include "mozilla/NullPtr.h"
+#include "mozilla/Util.h"
+#include "mozilla/Scoped.h"
+
+namespace mozilla {
+bool PoisonWriteEnabled();
+bool ValidWriteAssert(bool ok);
+bool IsDebugFile(intptr_t aFileID);
+intptr_t FileDescriptorToID(int aFd);
+}
+
+#endif
rename from xpcom/build/PoisonIOInterposerMac.cpp
rename to xpcom/build/mozPoisonWriteMac.cpp
--- a/xpcom/build/PoisonIOInterposerMac.cpp
+++ b/xpcom/build/mozPoisonWriteMac.cpp
@@ -1,247 +1,93 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim:set ts=4 sw=4 sts=4 ci et: */
 /* 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 "PoisonIOInterposer.h"
-#include "mach_override.h"
-
+#include "mozilla/mozPoisonWrite.h"
+#include "mozPoisonWriteBase.h"
+#include "mozilla/Util.h"
+#include "nsTraceRefcntImpl.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/IOInterposer.h"
+#include "mozilla/Scoped.h"
 #include "mozilla/Mutex.h"
-#include "mozilla/ProcessedStack.h"
-#include "mozilla/Scoped.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/Util.h"
-#include "nsPrintfCString.h"
+#include "mozilla/ProcessedStack.h"
 #include "nsStackWalk.h"
-#include "nsTraceRefcntImpl.h"
+#include "nsPrintfCString.h"
+#include "mach_override.h"
+#include "prio.h"
 #include "plstr.h"
-#include "prio.h"
-
+#include "nsCOMPtr.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "mozilla/SHA1.h"
+#include <sys/stat.h>
+#include <sys/socket.h>
 #include <vector>
 #include <algorithm>
 #include <string.h>
-
-#include <sys/stat.h>
-#include <sys/socket.h>
 #include <sys/uio.h>
 #include <aio.h>
 #include <dlfcn.h>
 
 namespace {
-
 using namespace mozilla;
 
-// Bit tracking if poisoned writes are enabled
-static bool sIsEnabled = false;
-
-// Check if writes are dirty before reporting IO
-static bool sOnlyReportDirtyWrites = false;
-
-// Routines for write validation
-bool IsValidWrite(int fd, const void *wbuf, size_t count);
-bool IsIPCWrite(int fd, const struct stat &buf);
-
-/******************************** IO AutoTimer ********************************/
-
-/**
- * RAII class for timing the duration of an I/O call and reporting the result
- * to the IOInterposeObserver API.
- */
-class MacIOAutoObservation : public IOInterposeObserver::Observation
-{
-public:
-  MacIOAutoObservation(IOInterposeObserver::Operation aOp,
-                       const char* aReference, int aFd)
-    : mFd(aFd),
-      mShouldObserve(sIsEnabled && IOInterposer::IsObservedOperation(aOp) &&
-                     !IsDebugFile(aFd))
-  {
-    if (mShouldObserve) {
-      mOperation = aOp;
-      mReference = aReference;
-      mStart = TimeStamp::Now();
-    }
-  }
-
-  MacIOAutoObservation(IOInterposeObserver::Operation aOp,
-                       const char* aReference, int aFd, const void *aBuf,
-                       size_t aCount)
-    : mFd(aFd),
-      mShouldObserve(sIsEnabled && IOInterposer::IsObservedOperation(aOp) &&
-                     !IsDebugFile(aFd))
-  {
-    if (mShouldObserve) {
-      mShouldObserve = IsValidWrite(aFd, aBuf, aCount);
-      if (mShouldObserve) {
-        mOperation = aOp;
-        mReference = aReference;
-        mStart = TimeStamp::Now();
-      }
-    }
-  }
-
-  ~MacIOAutoObservation()
-  {
-    if (mShouldObserve) {
-      mEnd = TimeStamp::Now();
-
-      // Report this observation
-      IOInterposer::Report(*this);
-    }
-  }
-
-private:
-  int                 mFd;
-  bool                mShouldObserve;
-};
-
-/****************************** Write Validation ******************************/
-
-// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
-// implemented with file descriptors, so filter them out.
-bool IsIPCWrite(int fd, const struct stat &buf) {
-  if ((buf.st_mode & S_IFMT) == S_IFIFO) {
-    return true;
-  }
-
-  if ((buf.st_mode & S_IFMT) != S_IFSOCK) {
-    return false;
-  }
-
-  sockaddr_storage address;
-  socklen_t len = sizeof(address);
-  if (getsockname(fd, (sockaddr*) &address, &len) != 0) {
-    return true; // Ignore the fd if we can't find what it is.
-  }
-
-  return address.ss_family == AF_UNIX;
-}
-
-// We want to report actual disk IO not things that don't move bits on the disk
-bool IsValidWrite(int fd, const void *wbuf, size_t count)
-{
-  // Ignore writes of zero bytes, Firefox does some during shutdown.
-  if (count == 0) {
-    return false;
-  }
-
-  {
-    struct stat buf;
-    int rv = fstat(fd, &buf);
-    if (rv != 0) {
-      return true;
-    }
-
-    if (IsIPCWrite(fd, buf)) {
-      return false;
-    }
-  }
-
-  // For writev we pass NULL in wbuf. We should only get here from
-  // dbm, and it uses write, so assert that we have wbuf.
-  if (!wbuf) {
-    return true;
-  }
-
-  // Break, here if we're allowed to report non-dirty writes
-  if(!sOnlyReportDirtyWrites) {
-    return true;
-  }
-
-  // As a really bad hack, accept writes that don't change the on disk
-  // content. This is needed because dbm doesn't keep track of dirty bits
-  // and can end up writing the same data to disk twice. Once when the
-  // user (nss) asks it to sync and once when closing the database.
-  ScopedFreePtr<void> wbuf2(malloc(count));
-  if (!wbuf2) {
-    return true;
-  }
-  off_t pos = lseek(fd, 0, SEEK_CUR);
-  if (pos == -1) {
-    return true;
-  }
-  ssize_t r = read(fd, wbuf2, count);
-  if (r < 0 || (size_t)r != count) {
-    return true;
-  }
-  int cmp = memcmp(wbuf, wbuf2, count);
-  if (cmp != 0) {
-    return true;
-  }
-  off_t pos2 = lseek(fd, pos, SEEK_SET);
-  if (pos2 != pos) {
-    return true;
-  }
-
-  // Otherwise this is not a valid write
-  return false;
-}
-
-/*************************** Function Interception  ***************************/
-
-/** Structure for declaration of function override */
 struct FuncData {
-  const char *Name;      // Name of the function for the ones we use dlsym
-  const void *Wrapper;   // The function that we will replace 'Function' with
-  void *Function;        // The function that will be replaced with 'Wrapper'
-  void *Buffer;          // Will point to the jump buffer that lets us call
-                         // 'Function' after it has been replaced.
+    const char *Name;      // Name of the function for the ones we use dlsym
+    const void *Wrapper;   // The function that we will replace 'Function' with
+    void *Function;        // The function that will be replaced with 'Wrapper'
+    void *Buffer;          // Will point to the jump buffer that lets us call
+                           // 'Function' after it has been replaced.
 };
 
 // Wrap aio_write. We have not seen it before, so just assert/report it.
 typedef ssize_t (*aio_write_t)(struct aiocb *aiocbp);
 ssize_t wrap_aio_write(struct aiocb *aiocbp);
 FuncData aio_write_data = { 0, (void*) wrap_aio_write, (void*) aio_write };
 ssize_t wrap_aio_write(struct aiocb *aiocbp) {
-  const char* ref = "aio_write";
-  MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref,
-                             aiocbp->aio_fildes);
-
-  aio_write_t old_write = (aio_write_t) aio_write_data.Buffer;
-  return old_write(aiocbp);
+    ValidWriteAssert(0);
+    aio_write_t old_write = (aio_write_t) aio_write_data.Buffer;
+    return old_write(aiocbp);
 }
 
 // Wrap pwrite-like functions.
 // We have not seen them before, so just assert/report it.
 typedef ssize_t (*pwrite_t)(int fd, const void *buf, size_t nbyte, off_t offset);
 template<FuncData &foo>
 ssize_t wrap_pwrite_temp(int fd, const void *buf, size_t nbyte, off_t offset) {
-  const char* ref = "pwrite_*";
-  MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd);
-  pwrite_t old_write = (pwrite_t) foo.Buffer;
-  return old_write(fd, buf, nbyte, offset);
+    ValidWriteAssert(0);
+    pwrite_t old_write = (pwrite_t) foo.Buffer;
+    return old_write(fd, buf, nbyte, offset);
 }
 
 // Define a FuncData for a pwrite-like functions.
 #define DEFINE_PWRITE_DATA(X, NAME)                                        \
 FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> };      \
 
 // This exists everywhere.
 DEFINE_PWRITE_DATA(pwrite, "pwrite")
 // These exist on 32 bit OS X
 DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
 DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003");
 // This exists on 64 bit OS X
 DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
 
+void AbortOnBadWrite(int fd, const void *wbuf, size_t count);
 
 typedef ssize_t (*writev_t)(int fd, const struct iovec *iov, int iovcnt);
 template<FuncData &foo>
 ssize_t wrap_writev_temp(int fd, const struct iovec *iov, int iovcnt) {
-  const char* ref = "pwrite_*";
-  MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd, nullptr,
-                             iovcnt);
-  writev_t old_write = (writev_t) foo.Buffer;
-  return old_write(fd, iov, iovcnt);
+    AbortOnBadWrite(fd, 0, iovcnt);
+    writev_t old_write = (writev_t) foo.Buffer;
+    return old_write(fd, iov, iovcnt);
 }
 
 // Define a FuncData for a writev-like functions.
 #define DEFINE_WRITEV_DATA(X, NAME)                                   \
 FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \
 
 // This exists everywhere.
 DEFINE_WRITEV_DATA(writev, "writev");
@@ -249,21 +95,19 @@ DEFINE_WRITEV_DATA(writev, "writev");
 DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
 DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003");
 // This exists on 64 bit OS X
 DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
 
 typedef ssize_t (*write_t)(int fd, const void *buf, size_t count);
 template<FuncData &foo>
 ssize_t wrap_write_temp(int fd, const void *buf, size_t count) {
-  const char* ref = "pwrite_*";
-  MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd, buf,
-                             count);
-  write_t old_write = (write_t) foo.Buffer;
-  return old_write(fd, buf, count);
+    AbortOnBadWrite(fd, buf, count);
+    write_t old_write = (write_t) foo.Buffer;
+    return old_write(fd, buf, count);
 }
 
 // Define a FuncData for a write-like functions.
 #define DEFINE_WRITE_DATA(X, NAME)                                   \
 FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \
 
 // This exists everywhere.
 DEFINE_WRITE_DATA(write, "write");
@@ -287,54 +131,104 @@ FuncData *Functions[] = { &aio_write_dat
 
                           &writev_data,
                           &writev_NOCANCEL_UNIX2003_data,
                           &writev_UNIX2003_data,
                           &writev_NOCANCEL_data};
 
 const int NumFunctions = ArrayLength(Functions);
 
-} // anonymous namespace
+// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
+// implemented with file descriptors, so filter them out.
+bool IsIPCWrite(int fd, const struct stat &buf) {
+    if ((buf.st_mode & S_IFMT) == S_IFIFO) {
+        return true;
+    }
+
+    if ((buf.st_mode & S_IFMT) != S_IFSOCK) {
+        return false;
+    }
+
+    sockaddr_storage address;
+    socklen_t len = sizeof(address);
+    if (getsockname(fd, (sockaddr*) &address, &len) != 0) {
+        return true; // Ignore the fd if we can't find what it is.
+    }
+
+    return address.ss_family == AF_UNIX;
+}
+
+void AbortOnBadWrite(int fd, const void *wbuf, size_t count) {
+    if (!PoisonWriteEnabled())
+        return;
+
+    // Ignore writes of zero bytes, firefox does some during shutdown.
+    if (count == 0)
+        return;
+
+    struct stat buf;
+    int rv = fstat(fd, &buf);
+    if (!ValidWriteAssert(rv == 0))
+        return;
 
-/******************************** IO Poisoning ********************************/
+    if (IsIPCWrite(fd, buf))
+        return;
+
+    // Debugging FDs are OK
+    if (IsDebugFile(fd))
+        return;
+
+    // For writev we pass nullptr in wbuf. We should only get here from
+    // dbm, and it uses write, so assert that we have wbuf.
+    if (!ValidWriteAssert(wbuf))
+        return;
+
+    // As a really bad hack, accept writes that don't change the on disk
+    // content. This is needed because dbm doesn't keep track of dirty bits
+    // and can end up writing the same data to disk twice. Once when the
+    // user (nss) asks it to sync and once when closing the database.
+    ScopedFreePtr<void> wbuf2(malloc(count));
+    if (!ValidWriteAssert(wbuf2))
+        return;
+    off_t pos = lseek(fd, 0, SEEK_CUR);
+    if (!ValidWriteAssert(pos != -1))
+        return;
+    ssize_t r = read(fd, wbuf2, count);
+    if (!ValidWriteAssert(r == static_cast<ssize_t>(count)))
+        return;
+    int cmp = memcmp(wbuf, wbuf2, count);
+    if (!ValidWriteAssert(cmp == 0))
+        return;
+    off_t pos2 = lseek(fd, pos, SEEK_SET);
+    if (!ValidWriteAssert(pos2 == pos))
+        return;
+}
+} // anonymous namespace
 
 namespace mozilla {
 
-void InitPoisonIOInterposer() {
-  // Enable reporting from poisoned write methods
-  sIsEnabled = true;
-
-  // Make sure we only poison writes once!
-  static bool WritesArePoisoned = false;
-  if (WritesArePoisoned) {
-    return;
-  }
-  WritesArePoisoned = true;
-
-  // stdout and stderr are OK.
-  MozillaRegisterDebugFD(1);
-  MozillaRegisterDebugFD(2);
-
-  for (int i = 0; i < NumFunctions; ++i) {
-    FuncData *d = Functions[i];
-    if (!d->Function) {
-      d->Function = dlsym(RTLD_DEFAULT, d->Name);
-    }
-    if (!d->Function) {
-      continue;
-    }
-    DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
-                                       &d->Buffer);
-    MOZ_ASSERT(t == err_none);
-  }
+intptr_t FileDescriptorToID(int aFd) {
+    return aFd;
 }
 
-void OnlyReportDirtyWrites() {
-  sOnlyReportDirtyWrites = true;
-}
+void PoisonWrite() {
+    // Quick sanity check that we don't poison twice.
+    static bool WritesArePoisoned = false;
+    MOZ_ASSERT(!WritesArePoisoned);
+    if (WritesArePoisoned)
+        return;
+    WritesArePoisoned = true;
+
+    if (!PoisonWriteEnabled())
+        return;
 
-void ClearPoisonIOInterposer() {
-  // Not sure how or if we can unpoison the functions. Would be nice, but no
-  // worries we won't need to do this anyway.
-  sIsEnabled = false;
+    for (int i = 0; i < NumFunctions; ++i) {
+        FuncData *d = Functions[i];
+        if (!d->Function)
+            d->Function = dlsym(RTLD_DEFAULT, d->Name);
+        if (!d->Function)
+            continue;
+        DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
+                                           &d->Buffer);
+        MOZ_ASSERT(t == err_none);
+    }
 }
-
-} // namespace mozilla
\ No newline at end of file
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/build/mozPoisonWriteStub.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 ci et: */
+/* 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 <stdio.h>
+namespace mozilla {
+void PoisonWrite() {
+}
+void DisableWritePoisoning() {
+}
+void EnableWritePoisoning() {
+}
+void InitWritePoisoning() {
+}
+}
+extern "C" {
+    void MozillaRegisterDebugFD(int fd) {
+    }
+    void MozillaRegisterDebugFILE(FILE *f) {
+    }
+    void MozillaUnRegisterDebugFD(int fd) {
+    }
+    void MozillaUnRegisterDebugFILE(FILE *f) {
+    }
+}
rename from xpcom/build/PoisonIOInterposerWin.cpp
rename to xpcom/build/mozPoisonWriteWin.cpp
--- a/xpcom/build/PoisonIOInterposerWin.cpp
+++ b/xpcom/build/mozPoisonWriteWin.cpp
@@ -1,222 +1,90 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 ci et: */
 /* 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 "PoisonIOInterposer.h"
-
-#include <algorithm>
 #include <stdio.h>
-#include <vector>
-
-#include <io.h>
 #include <windows.h>
-#include <winternl.h>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/IOInterposer.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/TimeStamp.h"
-#include "nsTArray.h"
+#include <winternl.h> // NTSTATUS
+#include <io.h>
 #include "nsWindowsDllInterceptor.h"
-#include "plstr.h"
-
-using namespace mozilla;
-
-namespace {
-
-// Keep track of poisoned state. Notice that there is no reason to lock access
-// to this variable as it's only changed in InitPoisonIOInterposer and
-// ClearPoisonIOInterposer which may only be called on the main-thread when no
-// other threads are running.
-static bool sIOPoisoned = false;
-
-/************************ Internal NT API Declarations ************************/
-
-/**
- * Function pointer declaration for internal NT routine to write data to file.
- * For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN.
- */
-typedef NTSTATUS (WINAPI *NtWriteFileFn)(
-  IN    HANDLE                  aFileHandle,
-  IN    HANDLE                  aEvent,
-  IN    PIO_APC_ROUTINE         aApc,
-  IN    PVOID                   aApcCtx,
-  OUT   PIO_STATUS_BLOCK        aIoStatus,
-  IN    PVOID                   aBuffer,
-  IN    ULONG                   aLength,
-  IN    PLARGE_INTEGER          aOffset,
-  IN    PULONG                  aKey
-);
-
-/**
- * Function pointer declaration for internal NT routine to write data to file.
- * No documentation exists, see wine sources for details.
- */
-typedef NTSTATUS (WINAPI *NtWriteFileGatherFn)(
-  IN    HANDLE                  aFileHandle,
-  IN    HANDLE                  aEvent,
-  IN    PIO_APC_ROUTINE         aApc,
-  IN    PVOID                   aApcCtx,
-  OUT   PIO_STATUS_BLOCK        aIoStatus,
-  IN    FILE_SEGMENT_ELEMENT*   aSegments,
-  IN    ULONG                   aLength,
-  IN    PLARGE_INTEGER          aOffset,
-  IN    PULONG                  aKey
-);
-
-/*************************** Auxiliary Declarations ***************************/
-
-/**
- * RAII class for timing the duration of an I/O call and reporting the result
- * to the IOInterposeObserver API.
- */
-class WinIOAutoObservation : public IOInterposeObserver::Observation
-{
-public:
-  WinIOAutoObservation(IOInterposeObserver::Operation aOp,
-                       const char* aReference, HANDLE aFileHandle)
-    : mFileHandle(aFileHandle),
-      mShouldObserve(IOInterposer::IsObservedOperation(aOp) &&
-                     !IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle)))
-  {
-    if (mShouldObserve) {
-      mOperation = aOp;
-      mReference = aReference;
-      mStart = TimeStamp::Now();
-    }
-  }
-
-  ~WinIOAutoObservation()
-  {
-    if (mShouldObserve) {
-      mEnd = TimeStamp::Now();
-      // Report this observation
-      IOInterposer::Report(*this);
-    }
-  }
-
-private:
-  HANDLE              mFileHandle;
-  bool                mShouldObserve;
-};
-
-/*************************** IO Interposing Methods ***************************/
-
-// Function pointers to original functions
-static NtWriteFileFn          gOriginalNtWriteFile;
-static NtWriteFileGatherFn    gOriginalNtWriteFileGather;
-
-// Interposed NtWriteFile function
-static NTSTATUS WINAPI InterposedNtWriteFile(
-  HANDLE                        aFileHandle,
-  HANDLE                        aEvent,
-  PIO_APC_ROUTINE               aApc,
-  PVOID                         aApcCtx,
-  PIO_STATUS_BLOCK              aIoStatus,
-  PVOID                         aBuffer,
-  ULONG                         aLength,
-  PLARGE_INTEGER                aOffset,
-  PULONG                        aKey)
-{
-  // Report IO
-  const char* ref = "NtWriteFile";
-  WinIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, aFileHandle);
-
-  // Something is badly wrong if this function is undefined
-  MOZ_ASSERT(gOriginalNtWriteFile);
-
-  // Execute original function
-  return gOriginalNtWriteFile(
-    aFileHandle,
-    aEvent,
-    aApc,
-    aApcCtx,
-    aIoStatus,
-    aBuffer,
-    aLength,
-    aOffset,
-    aKey
-  );
-}
-
-// Interposed NtWriteFileGather function
-static NTSTATUS WINAPI InterposedNtWriteFileGather(
-  HANDLE                        aFileHandle,
-  HANDLE                        aEvent,
-  PIO_APC_ROUTINE               aApc,
-  PVOID                         aApcCtx,
-  PIO_STATUS_BLOCK              aIoStatus,
-  FILE_SEGMENT_ELEMENT*         aSegments,
-  ULONG                         aLength,
-  PLARGE_INTEGER                aOffset,
-  PULONG                        aKey)
-{
-  // Report IO
-  const char* ref = "NtWriteFileGather";
-  WinIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, aFileHandle);
-
-  // Something is badly wrong if this function is undefined
-  MOZ_ASSERT(gOriginalNtWriteFileGather);
-
-  // Execute original function
-  return gOriginalNtWriteFileGather(
-    aFileHandle,
-    aEvent,
-    aApc,
-    aApcCtx,
-    aIoStatus,
-    aSegments,
-    aLength,
-    aOffset,
-    aKey
-  );
-}
-
-} // anonymous namespace
-
-/******************************** IO Poisoning ********************************/
-
-// Windows DLL interceptor
-static WindowsDllInterceptor sNtDllInterceptor;
+#include "mozilla/mozPoisonWrite.h"
+#include "mozPoisonWriteBase.h"
+#include "mozilla/Assertions.h"
 
 namespace mozilla {
 
-void InitPoisonIOInterposer() {
-  // Don't poison twice... as this function may only be invoked on the main
-  // thread when no other threads are running, it safe to allow multiple calls
-  // to InitPoisonIOInterposer() without complaining (ie. failing assertions).
-  if (sIOPoisoned) {
-    return;
-  }
-  sIOPoisoned = true;
+intptr_t FileDescriptorToID(int aFd) {
+  return _get_osfhandle(aFd);
+}
 
-  // Stdout and Stderr are OK.
-  MozillaRegisterDebugFD(1);
-  MozillaRegisterDebugFD(2);
+static WindowsDllInterceptor sNtDllInterceptor;
+
+void AbortOnBadWrite(HANDLE);
+bool ValidWriteAssert(bool ok);
 
-  // Initialize dll interceptor and add hooks
-  sNtDllInterceptor.Init("ntdll.dll");
-  sNtDllInterceptor.AddHook(
-    "NtWriteFile",
-    reinterpret_cast<intptr_t>(InterposedNtWriteFile),
-    reinterpret_cast<void**>(&gOriginalNtWriteFile)
-  );
-  sNtDllInterceptor.AddHook(
-    "NtWriteFileGather",
-    reinterpret_cast<intptr_t>(InterposedNtWriteFileGather),
-    reinterpret_cast<void**>(&gOriginalNtWriteFileGather)
-  );
+typedef NTSTATUS (WINAPI* WriteFile_fn)(HANDLE, HANDLE, PIO_APC_ROUTINE,
+                                        void*, PIO_STATUS_BLOCK,
+                                        const void*, ULONG, PLARGE_INTEGER,
+                                        PULONG);
+WriteFile_fn gOriginalWriteFile;
+
+static NTSTATUS WINAPI
+patched_WriteFile(HANDLE aFile, HANDLE aEvent, PIO_APC_ROUTINE aApc,
+                  void* aApcUser, PIO_STATUS_BLOCK aIoStatus,
+                  const void* aBuffer, ULONG aLength,
+                  PLARGE_INTEGER aOffset, PULONG aKey)
+{
+  AbortOnBadWrite(aFile);
+  return gOriginalWriteFile(aFile, aEvent, aApc, aApcUser, aIoStatus,
+                            aBuffer, aLength, aOffset, aKey);
 }
 
-void ClearPoisonIOInterposer() {
-  MOZ_ASSERT(false);
-  if (sIOPoisoned) {
-    // Destroy the DLL interceptor
-    sIOPoisoned = false;
-    sNtDllInterceptor = WindowsDllInterceptor();
-  }
+
+typedef NTSTATUS (WINAPI* WriteFileGather_fn)(HANDLE, HANDLE, PIO_APC_ROUTINE,
+                                              void*, PIO_STATUS_BLOCK,
+                                              FILE_SEGMENT_ELEMENT*,
+                                              ULONG, PLARGE_INTEGER, PULONG);
+WriteFileGather_fn gOriginalWriteFileGather;
+
+static NTSTATUS WINAPI
+patched_WriteFileGather(HANDLE aFile, HANDLE aEvent, PIO_APC_ROUTINE aApc,
+                        void* aApcUser, PIO_STATUS_BLOCK aIoStatus,
+                        FILE_SEGMENT_ELEMENT* aSegments, ULONG aLength,
+                        PLARGE_INTEGER aOffset, PULONG aKey)
+{
+  AbortOnBadWrite(aFile);
+  return gOriginalWriteFileGather(aFile, aEvent, aApc, aApcUser, aIoStatus,
+                                  aSegments, aLength, aOffset, aKey);
 }
 
-} // namespace mozilla
+void AbortOnBadWrite(HANDLE aFile)
+{
+  if (!PoisonWriteEnabled())
+    return;
+
+  // Debugging files are OK.
+  if (IsDebugFile(reinterpret_cast<intptr_t>(aFile)))
+    return;
+
+  ValidWriteAssert(false);
+}
+
+void PoisonWrite() {
+  // Quick sanity check that we don't poison twice.
+  static bool WritesArePoisoned = false;
+  MOZ_ASSERT(!WritesArePoisoned);
+  if (WritesArePoisoned)
+    return;
+  WritesArePoisoned = true;
+
+  if (!PoisonWriteEnabled())
+    return;
+
+  sNtDllInterceptor.Init("ntdll.dll");
+  sNtDllInterceptor.AddHook("NtWriteFile", reinterpret_cast<intptr_t>(patched_WriteFile), reinterpret_cast<void**>(&gOriginalWriteFile));
+  sNtDllInterceptor.AddHook("NtWriteFileGather", reinterpret_cast<intptr_t>(patched_WriteFileGather), reinterpret_cast<void**>(&gOriginalWriteFileGather));
+}
+}
+
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -104,19 +104,17 @@ extern nsresult nsStringInputStreamConst
 #include <locale.h>
 #include "mozilla/Services.h"
 #include "mozilla/Omnijar.h"
 #include "mozilla/HangMonitor.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsChromeRegistry.h"
 #include "nsChromeProtocolHandler.h"
-#include "mozilla/IOInterposer.h"
-#include "mozilla/PoisonIOInterposer.h"
-#include "mozilla/LateWriteChecks.h"
+#include "mozilla/mozPoisonWrite.h"
 
 #include "mozilla/scache/StartupCache.h"
 
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
@@ -679,20 +677,20 @@ ShutdownXPCOM(nsIServiceManager* servMgr
         // all threads created using the thread manager (with the exception of
         // the main thread) have exited.
         nsThreadManager::get()->Shutdown();
 
         NS_ProcessPendingEvents(thread);
 
         HangMonitor::NotifyActivity();
 
-        // Late-write checks needs to find the profile directory, so it has to
+        // Write poisoning needs to find the profile directory, so it has to
         // be initialized before mozilla::services::Shutdown or (because of
         // xpcshell tests replacing the service) modules being unloaded.
-        mozilla::InitLateWriteChecks();
+        InitWritePoisoning();
 
         // We save the "xpcom-shutdown-loaders" observers to notify after
         // the observerservice is gone.
         if (observerService) {
             observerService->
                 EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID,
                                    getter_AddRefs(moduleLoaders));
 
@@ -750,25 +748,17 @@ ShutdownXPCOM(nsIServiceManager* servMgr
         moduleLoaders = nullptr;
     }
 
     nsCycleCollector_shutdown();
 
     PROFILER_MARKER("Shutdown xpcom");
     // If we are doing any shutdown checks, poison writes.
     if (gShutdownChecks != SCM_NOTHING) {
-        // Calling InitIOInterposer or InitPoisonIOInterposer twice doesn't
-        // cause any problems, they'll safely abort the initialization on their
-        // own initiative.
-        mozilla::IOInterposer::Init();
-        mozilla::InitPoisonIOInterposer();
-#ifdef XP_MACOSX
-        mozilla::OnlyReportDirtyWrites();
-#endif /* XP_MACOSX */
-        mozilla::BeginLateWriteChecks();
+        mozilla::PoisonWrite();
     }
 
     // Shutdown nsLocalFile string conversion
     NS_ShutdownLocalFile();
 #ifdef XP_UNIX
     NS_ShutdownNativeCharsetUtils();
 #endif
 
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -448,17 +448,17 @@ XRE_API(void,
 
 XRE_API(void,
         XRE_StartupTimelineRecord, (int aEvent, PRTime aWhen))
 
 XRE_API(void,
         XRE_InitOmnijar, (nsIFile* greOmni,
                           nsIFile* appOmni))
 XRE_API(void,
-        XRE_StopLateWriteChecks, (void))
+        XRE_DisableWritePoisoning, (void))
 
 #ifdef XP_WIN
 /**
  * Valid environment types for XRE_GetWindowsEnvironment.
  */
 enum WindowsEnvironmentType {
   WindowsEnvironmentType_Desktop = 0,
   WindowsEnvironmentType_Metro = 1