Bug 1287044 - Annotate pending IPC messages when OOM due to too many of them. r=billm
authorTing-Yu Chou <janus926@gmail.com>
Mon, 04 Jul 2016 13:36:34 +0800
changeset 306189 50ff01c81e0944458ff72b4e307cbe720c88c6b3
parent 306188 6951eb08d08305ba5ffc3b8d956d8689f7b97cc3
child 306190 b65b6c1811e51859b4a968616c275e6c39555798
push id30480
push usercbook@mozilla.com
push dateFri, 22 Jul 2016 09:58:20 +0000
treeherdermozilla-central@e0bc88708ffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1287044
milestone50.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 1287044 - Annotate pending IPC messages when OOM due to too many of them. r=billm
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -485,16 +485,19 @@ MessageChannel::MessageChannel(MessageLi
     mTimedOutMessagePriority(0),
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
     mIsWaitingForIncoming(false),
     mAbortOnError(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
     mPeerPid(-1)
+#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
+    , mPending(AnnotateAllocator<Message>(*this))
+#endif
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
 #ifdef OS_WIN
     mTopFrame = nullptr;
     mIsSyncWaitingOnNonMainThread = false;
 #endif
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -15,16 +15,20 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Vector.h"
 #include "mozilla/WeakPtr.h"
 #if defined(OS_WIN)
 #include "mozilla/ipc/Neutering.h"
 #endif // defined(OS_WIN)
 #include "mozilla/ipc/Transport.h"
+#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
+#include "mozilla/mozalloc_oom.h"
+#include "nsExceptionHandler.h"
+#endif
 #include "MessageLink.h"
 
 #include <deque>
 #include <stack>
 #include <math.h>
 
 namespace mozilla {
 namespace ipc {
@@ -452,19 +456,89 @@ class MessageChannel : HasResultCodes
     // NOT our worker thread.
     void AssertLinkThread() const
     {
         MOZ_RELEASE_ASSERT(mWorkerLoopID != MessageLoop::current()->id(),
                            "on worker thread but should not be!");
     }
 
   private:
-    typedef IPC::Message::msgid_t msgid_t;
+#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
+    // TODO: Remove the condition OS_WIN above once we move to GCC 5 or higher,
+    // the code will be able to get compiled as std::deque will meet C++11
+    // allocator requirements.
+    template<class T>
+    struct AnnotateAllocator
+    {
+      typedef T value_type;
+      AnnotateAllocator(MessageChannel& channel) : mChannel(channel) {}
+      template<class U> AnnotateAllocator(const AnnotateAllocator<U>& other) :
+        mChannel(other.mChannel) {}
+      template<class U> bool operator==(const AnnotateAllocator<U>&) { return true; }
+      template<class U> bool operator!=(const AnnotateAllocator<U>&) { return false; }
+      T* allocate(size_t n) {
+        void* p = ::operator new(n * sizeof(T), std::nothrow);
+        if (!p && n) {
+          // Sort the pending messages by its type, note the sorting algorithm
+          // has to be in-place to avoid memory allocation.
+          MessageQueue& q = mChannel.mPending;
+          std::sort(q.begin(), q.end(), [](const Message& a, const Message& b) {
+            return a.type() < b.type();
+          });
+
+          // Iterate over the sorted queue to find the message that has the
+          // highest number of count.
+          char* topName = nullptr;
+          char* curName = nullptr;
+          msgid_t topType = 0, curType = 0;
+          uint32_t topCount = 0, curCount = 0;
+          for (MessageQueue::iterator it = q.begin(); it != q.end(); ++it) {
+            Message &msg = *it;
+            if (msg.type() == curType) {
+              ++curCount;
+            } else {
+              if (curCount > topCount) {
+                topName = curName;
+                topType = curType;
+                topCount = curCount;
+              }
+              curName = const_cast<char*>(msg.name());
+              curType = msg.type();
+              curCount = 1;
+            }
+          }
+          // In case the last type is the top one.
+          if (curCount > topCount) {
+            topName = curName;
+            topType = curType;
+            topCount = curCount;
+          }
+
+          CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("NumberOfPendingIPC"),
+                                             nsPrintfCString("%zu", q.size()));
+          CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("TopPendingIPCCount"),
+                                             nsPrintfCString("%u", topCount));
+          CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("TopPendingIPCName"),
+                                             nsPrintfCString("%s(0x%x)", topName, topType));
+
+          mozalloc_handle_oom(n * sizeof(T));
+        }
+        return static_cast<T*>(p);
+      }
+      void deallocate(T* p, size_t n) {
+        ::operator delete(p);
+      }
+      MessageChannel& mChannel;
+    };
+    typedef std::deque<Message, AnnotateAllocator<Message>> MessageQueue;
+#else
     typedef std::deque<Message> MessageQueue;
+#endif
     typedef std::map<size_t, Message> MessageMap;
+    typedef IPC::Message::msgid_t msgid_t;
 
     // XXXkhuey this can almost certainly die.
     // All dequeuing tasks require a single point of cancellation,
     // which is handled via a reference-counted task.
     class RefCountedTask
     {
       public:
         explicit RefCountedTask(already_AddRefed<CancelableRunnable> aTask)