Bug 737857 - Report number of ghost windows in telemetry. r=njn
☠☠ backed out by abe59de81f87 ☠ ☠
authorJustin Lebar <justin.lebar@gmail.com>
Sat, 31 Mar 2012 15:39:31 -0700
changeset 94125 9e4d09efa335ed3150ed8ac0a4b15a7b5875c2a7
parent 94124 bd73daadcfe99bf35df61d03ec0d997511520b66
child 94126 9b278853a823643538d71843a75267f05fe1ec9c
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [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
extensions/universalchardet/tests/bug631751be_text.html
js/xpconnect/tests/mochitest/test_bug462428.html
mobile/android/components/UpdatePrompt.js
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
toolkit/mozapps/update/updater/progressui_null.cpp
--- 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);
 
@@ -513,37 +517,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;
@@ -587,25 +591,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/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -1183,16 +1183,17 @@ TelemetryImpl::GetRegisteredHistograms(J
   *ret = OBJECT_TO_JSVAL(info);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *ret)
 {
   Histogram *h;
+  printf("GetHistogramByName(%s)\n", PromiseFlatCString(name).get());
   nsresult rv = GetHistogramByName(name, &h);
   if (NS_FAILED(rv))
     return rv;
 
   return WrapAndReturnHistogram(h, cx, ret);
 }
 
 class TelemetrySessionData : public nsITelemetrySessionData
--- 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;