Bug 916640 - Add memory reporter for message manager listeners. r=njn
authorWilliam Chen <wchen@mozilla.com>
Fri, 11 Oct 2013 16:07:15 -0700
changeset 164370 8854b9662405a3ba43d731273b3800c24e6906aa
parent 164369 8e06a75a0d5a220d34c8ac1e1d6feec88d45ce90
child 164371 e807ddf2965f621d2948a44b7631ebd6ee5d5135
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs916640
milestone27.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 916640 - Add memory reporter for message manager listeners. r=njn
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -29,16 +29,17 @@
 #include "nsIDOMClassInfo.h"
 #include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
 #include "JavaScriptChild.h"
 #include "JavaScriptParent.h"
 #include "nsDOMLists.h"
+#include "nsPrintfCString.h"
 #include <algorithm>
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 # if defined(SendMessage)
@@ -1004,24 +1005,188 @@ nsFrameMessageManager::Disconnect(bool a
   mParentManager = nullptr;
   mCallback = nullptr;
   mOwnedCallback = nullptr;
   if (!mHandlingMessage) {
     mListeners.Clear();
   }
 }
 
+namespace {
+
+struct MessageManagerReferentCount {
+  MessageManagerReferentCount() : strong(0), weakAlive(0), weakDead(0) {}
+  size_t strong;
+  size_t weakAlive;
+  size_t weakDead;
+  nsCOMArray<nsIAtom> suspectMessages;
+  nsDataHashtable<nsPtrHashKey<nsIAtom>, uint32_t> messageCounter;
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace dom {
+
+class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
+protected:
+  static const size_t kSuspectReferentCount = 300;
+  void CountReferents(nsFrameMessageManager* aMessageManager,
+                      MessageManagerReferentCount* aReferentCount);
+};
+
+NS_IMPL_ISUPPORTS1(MessageManagerReporter, nsIMemoryReporter)
+
+void
+MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
+                                       MessageManagerReferentCount* aReferentCount)
+{
+  for (uint32_t i = 0; i < aMessageManager->mListeners.Length(); i++) {
+    const nsMessageListenerInfo& listenerInfo = aMessageManager->mListeners[i];
+
+    if (listenerInfo.mWeakListener) {
+      nsCOMPtr<nsISupports> referent =
+        do_QueryReferent(listenerInfo.mWeakListener);
+      if (referent) {
+        aReferentCount->weakAlive++;
+      } else {
+        aReferentCount->weakDead++;
+      }
+    } else {
+      aReferentCount->strong++;
+    }
+
+    uint32_t oldCount = 0;
+    aReferentCount->messageCounter.Get(listenerInfo.mMessage, &oldCount);
+    uint32_t currentCount = oldCount + 1;
+    aReferentCount->messageCounter.Put(listenerInfo.mMessage, currentCount);
+
+    // Keep track of messages that have a suspiciously large
+    // number of referents (symptom of leak).
+    if (currentCount == kSuspectReferentCount) {
+      aReferentCount->suspectMessages.AppendElement(listenerInfo.mMessage);
+    }
+  }
+
+  // Add referent count in child managers because the listeners
+  // participate in messages dispatched from parent message manager.
+  for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); i++) {
+    nsRefPtr<nsFrameMessageManager> mm =
+      static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]);
+    CountReferents(mm, aReferentCount);
+  }
+}
+
+NS_IMETHODIMP
+MessageManagerReporter::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("message-manager");
+  return NS_OK;
+}
+
+static nsresult
+ReportReferentCount(const char* aManagerType,
+                    const MessageManagerReferentCount& aReferentCount,
+                    nsIMemoryReporterCallback* aCb,
+                    nsISupports* aClosure)
+{
+#define REPORT(_path, _amount, _desc)                                         \
+    do {                                                                      \
+      nsresult rv;                                                            \
+      rv = aCb->Callback(EmptyCString(), _path,                               \
+                         nsIMemoryReporter::KIND_OTHER,                       \
+                         nsIMemoryReporter::UNITS_COUNT, _amount,             \
+                         _desc, aClosure);                                    \
+      NS_ENSURE_SUCCESS(rv, rv);                                              \
+    } while (0)
+
+  REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
+         aReferentCount.strong,
+         nsPrintfCString("The number of strong referents held by the message "
+                         "manager in the %s manager.", aManagerType));
+  REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
+         aReferentCount.weakAlive,
+         nsPrintfCString("The number of weak referents that are still alive "
+                         "held by the message manager in the %s manager.",
+                         aManagerType));
+  REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
+         aReferentCount.weakDead,
+         nsPrintfCString("The number of weak referents that are dead "
+                         "held by the message manager in the %s manager.",
+                         aManagerType));
+
+  for (uint32_t i = 0; i < aReferentCount.suspectMessages.Length(); i++) {
+    uint32_t totalReferentCount = 0;
+    aReferentCount.messageCounter.Get(aReferentCount.suspectMessages[i],
+                                      &totalReferentCount);
+    nsAtomCString suspect(aReferentCount.suspectMessages[i]);
+    REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
+                           aManagerType, suspect.get()), totalReferentCount,
+           nsPrintfCString("A message in the %s message manager with a "
+                           "suspiciously large number of referents (symptom "
+                           "of a leak).", aManagerType));
+  }
+
+#undef REPORT
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb,
+                                       nsISupports* aClosure)
+{
+  nsresult rv;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    nsCOMPtr<nsIMessageBroadcaster> globalmm =
+      do_GetService("@mozilla.org/globalmessagemanager;1");
+    if (globalmm) {
+      nsRefPtr<nsFrameMessageManager> mm =
+        static_cast<nsFrameMessageManager*>(globalmm.get());
+      MessageManagerReferentCount count;
+      CountReferents(mm, &count);
+      rv = ReportReferentCount("global-manager", count, aCb, aClosure);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  if (nsFrameMessageManager::sParentProcessManager) {
+    MessageManagerReferentCount count;
+    CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
+    rv = ReportReferentCount("parent-process-manager", count, aCb, aClosure);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (nsFrameMessageManager::sChildProcessManager) {
+    MessageManagerReferentCount count;
+    CountReferents(nsFrameMessageManager::sChildProcessManager, &count);
+    rv = ReportReferentCount("child-process-manager", count, aCb, aClosure);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
+
 nsresult
 NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default,
                  NS_ERROR_NOT_AVAILABLE);
   nsFrameMessageManager* mm = new nsFrameMessageManager(nullptr,
                                                         nullptr,
                                                         MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
+  NS_RegisterMemoryReporter(new MessageManagerReporter());
   return CallQueryInterface(mm, aResult);
 }
 
 nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>*
   nsFrameScriptExecutor::sCachedScripts = nullptr;
 nsScriptCacheCleaner* nsFrameScriptExecutor::sScriptCacheCleaner = nullptr;
 
 void
@@ -1533,16 +1698,17 @@ NS_NewChildProcessMessageManager(nsISync
   NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager,
                "Re-creating sChildProcessManager");
 
   MessageManagerCallback* cb;
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     cb = new SameChildProcessMessageManagerCallback();
   } else {
     cb = new ChildProcessMessageManagerCallback();
+    NS_RegisterMemoryReporter(new MessageManagerReporter());
   }
   nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
                                                         nullptr,
                                                         MM_PROCESSMANAGER | MM_OWNSCALLBACK);
   nsFrameMessageManager::sChildProcessManager = mm;
   return CallQueryInterface(mm, aResult);
 }
 
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -27,16 +27,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class ContentParent;
 class ContentChild;
 struct StructuredCloneData;
 class ClonedMessageData;
+class MessageManagerReporter;
 
 namespace ipc {
 
 enum MessageManagerFlags {
   MM_CHILD = 0,
   MM_CHROME = 1,
   MM_GLOBAL = 2,
   MM_PROCESSMANAGER = 4,
@@ -138,16 +139,17 @@ class MOZ_STACK_CLASS SameProcessCpowHol
     JS::Rooted<JSObject*> mObj;
 };
 
 class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager,
                                         public nsIMessageBroadcaster,
                                         public nsIFrameScriptLoader,
                                         public nsIProcessChecker
 {
+  friend class mozilla::dom::MessageManagerReporter;
   typedef mozilla::dom::StructuredCloneData StructuredCloneData;
 public:
   nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                         nsFrameMessageManager* aParentManager,
                         /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
   : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
     mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
     mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),