--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -8,26 +8,29 @@
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/Move.h"
+#include "mozilla/Mutex.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
#include "nsAppRunner.h"
-#include "mozilla/UniquePtr.h"
#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
+#include "nsDataHashtable.h"
#include "nsDebug.h"
#include "nsISupportsImpl.h"
-#include "nsContentUtils.h"
+#include "nsPrintfCString.h"
#include <math.h>
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
using namespace mozilla::tasktracer;
#endif
// Undo the damage done by mozzconf.h
@@ -497,24 +500,122 @@ public:
"unresolved-ipc-responses", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedResponses,
"Outstanding IPC async message responses that are still not resolved.");
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(PendingResponseReporter, nsIMemoryReporter)
+class ChannelCountReporter final : public nsIMemoryReporter
+{
+ ~ChannelCountReporter() = default;
+
+ struct ChannelCounts {
+ size_t mNow;
+ size_t mMax;
+
+ ChannelCounts() : mNow(0), mMax(0) { }
+
+ void Inc() {
+ ++mNow;
+ if (mMax < mNow) {
+ mMax = mNow;
+ }
+ }
+
+ void Dec() {
+ MOZ_ASSERT(mNow > 0);
+ --mNow;
+ }
+ };
+
+ using CountTable = nsDataHashtable<nsDepCharHashKey, ChannelCounts>;
+
+ static StaticMutex sChannelCountMutex;
+ static CountTable* sChannelCounts;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ StaticMutexAutoLock countLock(sChannelCountMutex);
+ if (!sChannelCounts) {
+ return NS_OK;
+ }
+ for (auto iter = sChannelCounts->Iter(); !iter.Done(); iter.Next()) {
+ nsPrintfCString pathNow("ipc-channels/%s", iter.Key());
+ nsPrintfCString pathMax("ipc-channels-peak/%s", iter.Key());
+ nsPrintfCString descNow("Number of IPC channels for"
+ " top-level actor type %s", iter.Key());
+ nsPrintfCString descMax("Peak number of IPC channels for"
+ " top-level actor type %s", iter.Key());
+
+ aHandleReport->Callback(EmptyCString(), pathNow, KIND_OTHER,
+ UNITS_COUNT, iter.Data().mNow, descNow,
+ aData);
+ aHandleReport->Callback(EmptyCString(), pathMax, KIND_OTHER,
+ UNITS_COUNT, iter.Data().mMax, descMax,
+ aData);
+ }
+ return NS_OK;
+ }
+
+ static void
+ Increment(const char* aName)
+ {
+ StaticMutexAutoLock countLock(sChannelCountMutex);
+ if (!sChannelCounts) {
+ sChannelCounts = new CountTable;
+ }
+ sChannelCounts->GetOrInsert(aName).Inc();
+ }
+
+ static void
+ Decrement(const char* aName)
+ {
+ StaticMutexAutoLock countLock(sChannelCountMutex);
+ MOZ_ASSERT(sChannelCounts);
+ sChannelCounts->GetOrInsert(aName).Dec();
+ }
+};
+
+StaticMutex ChannelCountReporter::sChannelCountMutex;
+ChannelCountReporter::CountTable* ChannelCountReporter::sChannelCounts;
+
+NS_IMPL_ISUPPORTS(ChannelCountReporter, nsIMemoryReporter)
+
+// In child processes, the first MessageChannel is created before
+// XPCOM is initialized enough to construct the memory reporter
+// manager. This retries every time a MessageChannel is constructed,
+// which is good enough in practice.
+template<class Reporter>
+static void TryRegisterStrongMemoryReporter()
+{
+ static Atomic<bool> registered;
+ if (registered.compareExchange(false, true)) {
+ RefPtr<Reporter> reporter = new Reporter();
+ if (NS_FAILED(RegisterStrongMemoryReporter(reporter))) {
+ registered = false;
+ }
+ }
+}
+
Atomic<size_t> MessageChannel::gUnresolvedResponses;
MessageChannel::MessageChannel(const char* aName,
IToplevelProtocol *aListener)
: mName(aName),
mListener(aListener),
mChannelState(ChannelClosed),
mSide(UnknownSide),
+ mIsCrossProcess(false),
mLink(nullptr),
mWorkerLoop(nullptr),
mChannelErrorTask(nullptr),
mWorkerThread(nullptr),
mTimeoutMs(kNoTimeout),
mInTimeoutSecondHalf(false),
mNextSeqno(0),
mLastSendError(SyncSendError::SendSuccess),
@@ -548,20 +649,18 @@ MessageChannel::MessageChannel(const cha
this,
&MessageChannel::DispatchOnChannelConnected);
#ifdef OS_WIN
mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
#endif
- static Atomic<bool> registered;
- if (registered.compareExchange(false, true)) {
- RegisterStrongMemoryReporter(new PendingResponseReporter());
- }
+ TryRegisterStrongMemoryReporter<PendingResponseReporter>();
+ TryRegisterStrongMemoryReporter<ChannelCountReporter>();
}
MessageChannel::~MessageChannel()
{
MOZ_COUNT_DTOR(ipc::MessageChannel);
IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
#ifdef OS_WIN
if (mEvent) {
@@ -744,16 +843,19 @@ MessageChannel::Clear()
gUnresolvedResponses -= mPendingResponses.size();
for (auto& pair : mPendingResponses) {
pair.second.get()->Reject(ResponseRejectReason::ChannelClosed);
}
mPendingResponses.clear();
mWorkerLoop = nullptr;
+ if (mLink != nullptr && mIsCrossProcess) {
+ ChannelCountReporter::Decrement(mName);
+ }
delete mLink;
mLink = nullptr;
mOnChannelConnectedTask->Cancel();
if (mChannelErrorTask) {
mChannelErrorTask->Cancel();
mChannelErrorTask = nullptr;
@@ -782,16 +884,18 @@ MessageChannel::Open(Transport* aTranspo
mWorkerLoop = MessageLoop::current();
mWorkerThread = GetCurrentVirtualThread();
mWorkerLoop->AddDestructionObserver(this);
mListener->SetIsMainThreadProtocol();
ProcessLink *link = new ProcessLink(this);
link->Open(aTransport, aIOLoop, aSide); // :TODO: n.b.: sets mChild
mLink = link;
+ mIsCrossProcess = true;
+ ChannelCountReporter::Increment(mName);
return true;
}
bool
MessageChannel::Open(MessageChannel *aTargetChan, nsIEventTarget *aEventTarget, Side aSide)
{
// Opens a connection to another thread in the same process.