Bug 733495 - Monitor ullAvailPageFile (available commit space) on Windows, and fire a low-memory event when it gets low. r=bsmedberg
authorJustin Lebar <justin.lebar@gmail.com>
Thu, 08 Mar 2012 14:44:20 -0500
changeset 88568 cad7f1ab3594d632a8fa12565d39aa268bad5e8b
parent 88567 37441c99ccf11ca858150bb65c9540e85789a227
child 88569 5774e47361404a5c860d5a828a88576ad0d50b76
push id22208
push usermak77@bonardo.net
push dateFri, 09 Mar 2012 12:34:50 +0000
treeherdermozilla-central@ead9016b4102 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs733495
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 733495 - Monitor ullAvailPageFile (available commit space) on Windows, and fire a low-memory event when it gets low. r=bsmedberg
modules/libpref/src/init/all.js
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
xpcom/base/AvailableMemoryTracker.cpp
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3457,16 +3457,20 @@ pref("profiler.entries", 100000);
 pref("dom.network.enabled", true);
 pref("dom.network.metered", false);
 #ifdef XP_WIN
 // On 32-bit Windows, fire a low-memory notification if we have less than this
 // many mb of virtual address space available.
 pref("memory.low_virtual_memory_threshold_mb", 128);
 
 // On Windows 32- or 64-bit, fire a low-memory notification if we have less
+// than this many mb of commit space (physical memory plus page file) left.
+pref("memory.low_commit_space_threshold_mb", 128);
+
+// On Windows 32- or 64-bit, fire a low-memory notification if we have less
 // than this many mb of physical memory available on the whole machine.
 pref("memory.low_physical_memory_threshold_mb", 0);
 
 // On Windows 32- or 64-bit, don't fire a low-memory notification because of
-// low available physical memory more than once every
-// low_physical_memory_notification_interval_ms.
-pref("memory.low_physical_memory_notification_interval_ms", 10000);
+// low available physical memory or low commit space more than once every
+// low_memory_notification_interval_ms.
+pref("memory.low_memory_notification_interval_ms", 10000);
 #endif
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -99,16 +99,17 @@ HISTOGRAM(MEMORY_STORAGE_SQLITE, 1024, 5
 HISTOGRAM(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Memory used for uncompressed, in-use content images (KB)")
 HISTOGRAM(MEMORY_HEAP_ALLOCATED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Heap memory allocated (KB)")
 HISTOGRAM(MEMORY_EXPLICIT, 1024, 1024 * 1024, 50, EXPONENTIAL, "Explicit memory allocations (KB)")
 #if defined(XP_MACOSX)
 HISTOGRAM(MEMORY_FREE_PURGED_PAGES_MS, 1, 1024, 10, EXPONENTIAL, "Time(ms) to purge MADV_FREE'd heap pages.")
 #elif defined(XP_WIN)
 HISTOGRAM(LOW_MEMORY_EVENTS_VIRTUAL, 1, 1024, 21, EXPONENTIAL, "Number of low-virtual-memory events fired since last ping")
 HISTOGRAM(LOW_MEMORY_EVENTS_PHYSICAL, 1, 1024, 21, EXPONENTIAL, "Number of low-physical-memory events fired since last ping")
+HISTOGRAM(LOW_MEMORY_EVENTS_COMMIT_SPACE, 1, 1024, 21, EXPONENTIAL, "Number of low-commit-space events fired since last ping")
 #endif
 
 #if defined(XP_WIN)
 HISTOGRAM(EARLY_GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount before glue startup")
 HISTOGRAM(EARLY_GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount before glue startup (KB)")
 HISTOGRAM(GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount after glue startup")
 HISTOGRAM(GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount after glue startup (KB)")
 #elif defined(XP_UNIX)
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -62,16 +62,17 @@ const MEM_HISTOGRAMS = {
   "explicit": "MEMORY_EXPLICIT",
   "resident": "MEMORY_RESIDENT",
   "storage-sqlite": "MEMORY_STORAGE_SQLITE",
   "images-content-used-uncompressed":
     "MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED",
   "heap-allocated": "MEMORY_HEAP_ALLOCATED",
   "page-faults-hard": "PAGE_FAULTS_HARD",
   "low-memory-events-virtual": "LOW_MEMORY_EVENTS_VIRTUAL",
+  "low-memory-events-commit-space": "LOW_MEMORY_EVENTS_COMMIT_SPACE",
   "low-memory-events-physical": "LOW_MEMORY_EVENTS_PHYSICAL"
 };
 // Seconds of idle time before pinging.
 // On idle-daily a gather-telemetry notification is fired, during it probes can
 // start asynchronous tasks to gather data.  On the next idle the data is sent.
 const IDLE_TIMEOUT_SECONDS = 5 * 60;
 
 var gLastMemoryPoll = null;
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -136,20 +136,22 @@ void safe_write(PRUint64 x)
       safe_write("\n");                         \
     }                                           \
   } while(0)
 #else
 #define DEBUG_WARN_IF_FALSE(cond, msg)
 #endif
 
 PRUint32 sLowVirtualMemoryThreshold = 0;
+PRUint32 sLowCommitSpaceThreshold = 0;
 PRUint32 sLowPhysicalMemoryThreshold = 0;
-PRUint32 sLowPhysicalMemoryNotificationIntervalMS = 0;
+PRUint32 sLowMemoryNotificationIntervalMS = 0;
 
 PRUint32 sNumLowVirtualMemEvents = 0;
+PRUint32 sNumLowCommitSpaceEvents = 0;
 PRUint32 sNumLowPhysicalMemEvents = 0;
 
 WindowsDllInterceptor sKernel32Intercept;
 WindowsDllInterceptor sGdi32Intercept;
 
 // Have we installed the kernel intercepts above?
 bool sHooksInstalled = false;
 
@@ -168,64 +170,76 @@ void* (WINAPI *sMapViewOfFileOrig)
    DWORD aFileOffsetHigh, DWORD aFileOffsetLow,
    SIZE_T aNumBytesToMap);
 
 HBITMAP (WINAPI *sCreateDIBSectionOrig)
   (HDC aDC, const BITMAPINFO *aBitmapInfo,
    UINT aUsage, VOID **aBits,
    HANDLE aSection, DWORD aOffset);
 
+/**
+ * Fire a memory pressure event if it's been long enough since the last one we
+ * fired.
+ */
+bool MaybeScheduleMemoryPressureEvent()
+{
+  // If this interval rolls over, we may fire an extra memory pressure
+  // event, but that's not a big deal.
+  PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
+  if (sHasScheduledOneLowMemoryNotification &&
+      PR_IntervalToMilliseconds(interval) < sLowMemoryNotificationIntervalMS) {
+
+    LOG("Not scheduling low physical memory notification, "
+        "because not enough time has elapsed since last one.");
+    return false;
+  }
+
+  // There's a bit of a race condition here, since an interval may be a
+  // 64-bit number, and 64-bit writes aren't atomic on x86-32.  But let's
+  // not worry about it -- the races only happen when we're already
+  // experiencing memory pressure and firing notifications, so the worst
+  // thing that can happen is that we fire two notifications when we
+  // should have fired only one.
+  sHasScheduledOneLowMemoryNotification = true;
+  sLastLowMemoryNotificationTime = PR_IntervalNow();
+
+  LOG("Scheduling memory pressure notification.");
+  ScheduleMemoryPressureEvent();
+  return true;
+}
+
 void CheckMemAvailable()
 {
   MEMORYSTATUSEX stat;
   stat.dwLength = sizeof(stat);
   bool success = GlobalMemoryStatusEx(&stat);
 
   DEBUG_WARN_IF_FALSE(success, "GlobalMemoryStatusEx failed.");
 
   if (success)
   {
     // sLowVirtualMemoryThreshold is in MB, but ullAvailVirtual is in bytes.
     if (stat.ullAvailVirtual < sLowVirtualMemoryThreshold * 1024 * 1024) {
-      // If we're running low on virtual memory, schedule the notification.
-      // We'll probably crash if we run out of virtual memory, so don't worry
-      // about firing this notification too often.
+      // If we're running low on virtual memory, unconditionally schedule the
+      // notification.  We'll probably crash if we run out of virtual memory,
+      // so don't worry about firing this notification too often.
       LOG("Detected low virtual memory.");
       PR_ATOMIC_INCREMENT(&sNumLowVirtualMemEvents);
       ScheduleMemoryPressureEvent();
     }
+    else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
+      LOG("Detected low available page file space.");
+      if (MaybeScheduleMemoryPressureEvent()) {
+        PR_ATOMIC_INCREMENT(&sNumLowCommitSpaceEvents);
+      }
+    }
     else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) {
       LOG("Detected low physical memory.");
-      // If the machine is running low on physical memory and it's been long
-      // enough since we last fired a low-memory notification, fire a
-      // notification.
-      //
-      // If this interval rolls over, we may fire an extra memory pressure
-      // event, but that's not a big deal.
-      PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
-      if (!sHasScheduledOneLowMemoryNotification ||
-          PR_IntervalToMilliseconds(interval) >=
-            sLowPhysicalMemoryNotificationIntervalMS) {
-
-        // There's a bit of a race condition here, since an interval may be a
-        // 64-bit number, and 64-bit writes aren't atomic on x86-32.  But let's
-        // not worry about it -- the races only happen when we're already
-        // experiencing memory pressure and firing notifications, so the worst
-        // thing that can happen is that we fire two notifications when we
-        // should have fired only one.
-        sHasScheduledOneLowMemoryNotification = true;
-        sLastLowMemoryNotificationTime = PR_IntervalNow();
-
-        LOG("Scheduling memory pressure notification.");
+      if (MaybeScheduleMemoryPressureEvent()) {
         PR_ATOMIC_INCREMENT(&sNumLowPhysicalMemEvents);
-        ScheduleMemoryPressureEvent();
-      }
-      else {
-        LOG("Not scheduling low physical memory notification, "
-            "because not enough time has elapsed since last one.");
       }
     }
   }
 }
 
 LPVOID WINAPI
 VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
                  DWORD aAllocationType,
@@ -384,16 +398,59 @@ public:
         sLowVirtualMemoryThreshold));
     }
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(NumLowVirtualMemoryEventsMemoryReporter, nsIMemoryReporter)
 
+class NumLowCommitSpaceEventsMemoryReporter : public NumLowMemoryEventsReporter
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD GetPath(nsACString &aPath)
+  {
+    aPath.AssignLiteral("low-commit-space-events");
+    return NS_OK;
+  }
+
+  NS_IMETHOD GetAmount(PRInt64 *aAmount)
+  {
+    *aAmount = sNumLowCommitSpaceEvents;
+    return NS_OK;
+  }
+
+  NS_IMETHOD GetDescription(nsACString &aDescription)
+  {
+    aDescription.AssignLiteral(
+      "Number of low-commit-space events fired since startup. ");
+
+    if (sLowCommitSpaceThreshold == 0 || !sHooksInstalled) {
+      aDescription.Append(nsPrintfCString(1024,
+        "Tracking low-commit-space events is disabled, but you can enable it "
+        "by giving the memory.low_commit_space_threshold_mb pref a non-zero "
+        "value%s.",
+        sHooksInstalled ? "" : " and restarting"));
+    }
+    else {
+      aDescription.Append(nsPrintfCString(1024,
+        "We fire such an event if we notice there is less than %d MB of "
+        "available commit space (controlled by the "
+        "'memory.low_commit_space_threshold_mb' pref).  Windows will likely "
+        "kill us if we run out of commit space, so this event is somewhat dire.",
+        sLowCommitSpaceThreshold));
+    }
+    return NS_OK;
+  }
+};
+
+NS_IMPL_ISUPPORTS1(NumLowCommitSpaceEventsMemoryReporter, nsIMemoryReporter)
+
 class NumLowPhysicalMemoryEventsMemoryReporter : public NumLowMemoryEventsReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetPath(nsACString &aPath)
   {
     aPath.AssignLiteral("low-memory-events-physical");
@@ -447,18 +504,20 @@ void Init()
   }
   else {
     Preferences::AddUintVarCache(&sLowVirtualMemoryThreshold,
         "memory.low_virtual_mem_threshold_mb", 128);
   }
 
   Preferences::AddUintVarCache(&sLowPhysicalMemoryThreshold,
       "memory.low_physical_memory_threshold_mb", 0);
-  Preferences::AddUintVarCache(&sLowPhysicalMemoryNotificationIntervalMS,
-      "memory.low_physical_memory_notification_interval_ms", 10000);
+  Preferences::AddUintVarCache(&sLowCommitSpaceThreshold,
+      "memory.low_commit_space_threshold_mb", 128);
+  Preferences::AddUintVarCache(&sLowMemoryNotificationIntervalMS,
+      "memory.low_memory_notification_interval_ms", 10000);
 
   // Don't register the hooks if we're a build instrumented for PGO or if both
   // thresholds are 0.  (If we're an instrumented build, the compiler adds
   // function calls all over the place which may call VirtualAlloc; this makes
   // it hard to prevent VirtualAllocHook from reentering itself.)
 
   if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED") &&
       (sLowVirtualMemoryThreshold != 0 || sLowPhysicalMemoryThreshold != 0)) {
@@ -475,16 +534,17 @@ void Init()
     sGdi32Intercept.AddHook("CreateDIBSection",
       reinterpret_cast<intptr_t>(CreateDIBSectionHook),
       (void**) &sCreateDIBSectionOrig);
   }
   else {
     sHooksInstalled = false;
   }
 
+  NS_RegisterMemoryReporter(new NumLowCommitSpaceEventsMemoryReporter());
   NS_RegisterMemoryReporter(new NumLowPhysicalMemoryEventsMemoryReporter());
   if (sizeof(void*) == 4) {
     NS_RegisterMemoryReporter(new NumLowVirtualMemoryEventsMemoryReporter());
   }
 }
 
 } // namespace AvailableMemoryTracker
 } // namespace mozilla