Bug 737857 - Report number of ghost windows in telemetry. r=njn
authorJustin Lebar <justin.lebar@gmail.com>
Mon, 02 Apr 2012 10:37:04 -0400
changeset 90842 80df30079d1dd4172349896b834da43de0731d19
parent 90841 dabe904f3a4212c206459eec474eae5f0180c11f
child 90843 0501a5506c87a09e1d5c33767da23796a55105d9
push id22394
push userMs2ger@gmail.com
push dateTue, 03 Apr 2012 07:22:53 +0000
treeherdermozilla-central@9894cd999781 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs737857
milestone14.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 737857 - Report number of ghost windows in telemetry. r=njn
dom/base/nsWindowMemoryReporter.cpp
dom/base/nsWindowMemoryReporter.h
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -67,19 +67,23 @@ nsWindowMemoryReporter::Init()
     // DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
     // when a window's docshell is set to NULL.
     os->AddObserver(windowReporter, DOM_WINDOW_DESTROYED_TOPIC,
                     /* weakRef = */ true);
     os->AddObserver(windowReporter, "after-minimize-memory-usage",
                     /* weakRef = */ true);
   }
 
-  nsGhostWindowMemoryReporter *ghostReporter =
-    new nsGhostWindowMemoryReporter(windowReporter);
-  NS_RegisterMemoryMultiReporter(ghostReporter);
+  GhostURLsReporter *ghostMultiReporter =
+    new GhostURLsReporter(windowReporter);
+  NS_RegisterMemoryMultiReporter(ghostMultiReporter);
+
+  NumGhostsReporter *ghostReporter =
+    new NumGhostsReporter(windowReporter);
+  NS_RegisterMemoryReporter(ghostReporter);
 }
 
 static already_AddRefed<nsIURI>
 GetWindowURI(nsIDOMWindow *aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(pWindow, NULL);
 
@@ -517,37 +521,37 @@ nsWindowMemoryReporter::CheckForGhostWin
   // if it's not null.
   CheckForGhostWindowsEnumeratorData ghostEnumData =
     { &nonDetachedWindowDomains, aOutGhostIDs, tldService,
       GetGhostTimeout(), TimeStamp::Now() };
   mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
                              &ghostEnumData);
 }
 
-NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::nsGhostWindowMemoryReporter,
+NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::GhostURLsReporter,
                    nsIMemoryMultiReporter)
 
 nsWindowMemoryReporter::
-nsGhostWindowMemoryReporter::nsGhostWindowMemoryReporter(
+GhostURLsReporter::GhostURLsReporter(
   nsWindowMemoryReporter* aWindowReporter)
   : mWindowReporter(aWindowReporter)
 {
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
-nsGhostWindowMemoryReporter::GetName(nsACString& aName)
+GhostURLsReporter::GetName(nsACString& aName)
 {
   aName.AssignLiteral("ghost-windows");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
-nsGhostWindowMemoryReporter::GetExplicitNonHeap(PRInt64* aOut)
+GhostURLsReporter::GetExplicitNonHeap(PRInt64* aOut)
 {
   *aOut = 0;
   return NS_OK;
 }
 
 struct ReportGhostWindowsEnumeratorData
 {
   nsIMemoryMultiReporterCallback* callback;
@@ -591,25 +595,98 @@ ReportGhostWindowsEnumerator(nsUint64Has
     data->rv = rv;
   }
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
-nsGhostWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
-                                           nsISupports* aClosure)
+GhostURLsReporter::CollectReports(
+  nsIMemoryMultiReporterCallback* aCb,
+  nsISupports* aClosure)
 {
   // Get the IDs of all the ghost windows in existance.
   nsTHashtable<nsUint64HashKey> ghostWindows;
   ghostWindows.Init();
   mWindowReporter->CheckForGhostWindows(&ghostWindows);
 
   ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
     { aCb, aClosure, NS_OK };
 
   // Call aCb->Callback() for each ghost window.
   ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
                                 &reportGhostWindowsEnumData);
 
   return reportGhostWindowsEnumData.rv;
 }
+
+NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::NumGhostsReporter,
+                   nsIMemoryReporter)
+
+nsWindowMemoryReporter::
+NumGhostsReporter::NumGhostsReporter(
+  nsWindowMemoryReporter *aWindowReporter)
+  : mWindowReporter(aWindowReporter)
+{}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+NumGhostsReporter::GetProcess(nsACString& aProcess)
+{
+  aProcess.AssignLiteral("");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+NumGhostsReporter::GetPath(nsACString& aPath)
+{
+  aPath.AssignLiteral("ghost-windows");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+NumGhostsReporter::GetKind(PRInt32* aKind)
+{
+  *aKind = KIND_OTHER;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+NumGhostsReporter::GetUnits(PRInt32* aUnits)
+{
+  *aUnits = nsIMemoryReporter::UNITS_COUNT;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+NumGhostsReporter::GetDescription(nsACString& aDesc)
+{
+  nsPrintfCString str(1024,
+"The number of ghost windows present (the number of nodes underneath \
+explicit/window-objects/top(none)/ghost, modulo race conditions).  A ghost \
+window is not shown in any tab, does not share a domain with any non-detached \
+windows, and has met these criteria for at least %ds \
+(memory.ghost_window_timeout_seconds) or has survived a round of about:memory's \
+minimize memory usage button.\n\n\
+Ghost windows can happen legitimately, but they are often indicative of leaks \
+in the browser or add-ons.",
+  mWindowReporter->GetGhostTimeout());
+
+  aDesc.Assign(str);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+NumGhostsReporter::GetAmount(PRInt64* aAmount)
+{
+  nsTHashtable<nsUint64HashKey> ghostWindows;
+  ghostWindows.Init();
+  mWindowReporter->CheckForGhostWindows(&ghostWindows);
+
+  *aAmount = ghostWindows.Count();
+  return NS_OK;
+}
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -136,32 +136,49 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYMULTIREPORTER
   NS_DECL_NSIOBSERVER
 
   static void Init();
 
 private:
   /**
-   * nsGhostWindowMemoryReporter generates the "ghost-windows" memory report.
-   * If you're only interested in the list of ghost windows, running this
-   * report is faster than running nsWindowMemoryReporter.
+   * GhostURLsReporter generates the "ghost-windows" multi-report, which
+   * includes a list of all ghost windows' URLs.  If you're only interested in
+   * this list, running this report is faster than running
+   * nsWindowMemoryReporter.
    */
-  class nsGhostWindowMemoryReporter: public nsIMemoryMultiReporter
+  class GhostURLsReporter: public nsIMemoryMultiReporter
   {
   public:
-    nsGhostWindowMemoryReporter(nsWindowMemoryReporter* aWindowReporter);
+    GhostURLsReporter(nsWindowMemoryReporter* aWindowReporter);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIMEMORYMULTIREPORTER
 
   private:
     nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
   };
 
+  /**
+   * nsGhostWindowReporter generates the "ghost-windows" single-report, which
+   * counts the number of ghost windows present.
+   */
+  class NumGhostsReporter: public nsIMemoryReporter
+  {
+  public:
+    NumGhostsReporter(nsWindowMemoryReporter* aWindowReporter);
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIMEMORYREPORTER
+
+  private:
+    nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
+  };
+
   // Protect ctor, use Init() instead.
   nsWindowMemoryReporter();
 
   /**
    * Get the number of seconds for which a window must satisfy ghost criteria
    * (1) and (2) before we deem that it satisfies criterion (3).
    */
   PRUint32 GetGhostTimeout();
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -258,17 +258,17 @@ function processMemoryReporters(aMgr, aI
       handleException("multi-reporter", name, e);
     }
   }
 }
 
 // This regexp matches sentences and sentence fragments, i.e. strings that
 // start with a capital letter and ends with a '.'.  (The final sentence may be
 // in parentheses, so a ')' might appear after the '.'.)
-const gSentenceRegExp = /^[A-Z].*\.\)?$/;
+const gSentenceRegExp = /^[A-Z].*\.\)?$/m;
 
 function checkReport(aUnsafePath, aKind, aUnits, aAmount, aDescription)
 {
   if (aUnsafePath.startsWith("explicit/")) {
     assert(aKind === KIND_HEAP || aKind === KIND_NONHEAP, "bad explicit kind");
     assert(aUnits === UNITS_BYTES, "bad explicit units");
     assert(aDescription.match(gSentenceRegExp),
            "non-sentence explicit description");
@@ -282,17 +282,17 @@ function checkReport(aUnsafePath, aKind,
     assert(!aUnsafePath.startsWith("explicit/") &&
            !aUnsafePath.startsWith("smaps/"),
            "bad SUMMARY path");
 
   } else {
     assert(aUnsafePath.indexOf("/") === -1, "'other' path contains '/'");
     assert(aKind === KIND_OTHER, "bad other kind: " + aUnsafePath);
     assert(aDescription.match(gSentenceRegExp),
-           "non-sentence other description");
+           "non-sentence other description " + aDescription);
   }
 }
 
 //---------------------------------------------------------------------------
 
 function clearBody()
 {
   let oldBody = document.body;
@@ -1694,17 +1694,17 @@ function getGhostWindowsByProcess(aMgr)
   }
 
   let ghostWindowsByProcess = {};
 
   function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
                         aDescription)
   {
     let unsafeSplit = aUnsafePath.split('/');
-    assert(unsafeSplit[0] == 'ghost-windows/',
+    assert(unsafeSplit[0] == 'ghost-windows',
            'Unexpected path in getGhostWindowsByProcess: ' + aUnsafePath);
 
     let unsafeURL = unsafeSplit[1];
     let ghostWindow = new GhostWindow(unsafeURL);
 
     let process = aProcess === "" ? "Main" : aProcess;
     if (!ghostWindowsByProcess[process]) {
       ghostWindowsByProcess[process] = {};
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -97,16 +97,17 @@ HISTOGRAM_BOOLEAN(TELEMETRY_SUCCESS,  "S
 HISTOGRAM(MEMORY_JS_COMPARTMENTS_SYSTEM, 1, 1000, 50, EXPONENTIAL, "Total JavaScript compartments used for add-ons and internals.")
 HISTOGRAM(MEMORY_JS_COMPARTMENTS_USER, 1, 1000, 50, EXPONENTIAL, "Total JavaScript compartments used for web pages")
 HISTOGRAM(MEMORY_JS_GC_HEAP, 1024, 512 * 1024, 50, EXPONENTIAL, "Memory used by the garbage-collected JavaScript heap (KB)")
 HISTOGRAM(MEMORY_RESIDENT, 32 * 1024, 1024 * 1024, 50, EXPONENTIAL, "Resident memory size (KB)")
 HISTOGRAM(MEMORY_STORAGE_SQLITE, 1024, 512 * 1024, 50, EXPONENTIAL, "Memory used by SQLite (KB)")
 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)")
+HISTOGRAM(GHOST_WINDOWS, 1, 128, 8, EXPONENTIAL, "Number of ghost windows")
 #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
 
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -63,17 +63,18 @@ const MEM_HISTOGRAMS = {
   "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"
+  "low-memory-events-physical": "LOW_MEMORY_EVENTS_PHYSICAL",
+  "ghost-windows": "GHOST_WINDOWS"
 };
 // 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;