зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1475899: Part 4 - Add memory reporter for committed thread stack sizes on Linux. r=erahm
Later patches add support for Windows. OS-X will need a follow-up. MozReview-Commit-ID: DDd6uir4KzM --HG-- extra : source : b4394660fde2b05e972c491246570d8f79d8a7c6 extra : absorb_source : 063bc5db64faa9311ac9db99259cd477fb8fcf7e
This commit is contained in:
Родитель
fb98e4016d
Коммит
1dbd559f77
|
@ -29,6 +29,7 @@
|
|||
#include "mozilla/MemoryReportingProcess.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
|
@ -56,6 +57,9 @@ using namespace dom;
|
|||
|
||||
#if defined(XP_LINUX)
|
||||
|
||||
#include "mozilla/MemoryMapping.h"
|
||||
#include "nsThread.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -1398,6 +1402,108 @@ public:
|
|||
};
|
||||
NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
|
||||
|
||||
#ifdef XP_LINUX
|
||||
class ThreadStacksReporter final : public nsIMemoryReporter
|
||||
{
|
||||
~ThreadStacksReporter() = default;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) override
|
||||
{
|
||||
nsTArray<MemoryMapping> mappings(1024);
|
||||
MOZ_TRY(GetMemoryMappings(mappings));
|
||||
|
||||
// Enumerating over active threads requires holding a lock, so we collect
|
||||
// info on all threads, and then call our reporter callbacks after releasing
|
||||
// the lock.
|
||||
struct ThreadData
|
||||
{
|
||||
nsCString mName;
|
||||
uint32_t mThreadId;
|
||||
size_t mPrivateSize;
|
||||
};
|
||||
AutoTArray<ThreadData, 32> threads;
|
||||
|
||||
for (auto* thread : nsThread::Enumerate()) {
|
||||
if (!thread->StackBase()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = mappings.BinaryIndexOf(thread->StackBase());
|
||||
if (idx < 0) {
|
||||
continue;
|
||||
}
|
||||
// Referenced() is the combined size of all pages in the region which have
|
||||
// ever been touched, and are therefore consuming memory. For stack
|
||||
// regions, these pages are guaranteed to be un-shared unless we fork
|
||||
// after creating threads (which we don't).
|
||||
size_t privateSize = mappings[idx].Referenced();
|
||||
|
||||
// On Linux, we have to be very careful matching memory regions to thread
|
||||
// stacks.
|
||||
//
|
||||
// To begin with, the kernel only reports VM stats for regions of all
|
||||
// adjacent pages with the same flags, protection, and backing file.
|
||||
// There's no way to get finer-grained usage information for a subset of
|
||||
// those pages.
|
||||
//
|
||||
// Stack segments always have a guard page at the bottom of the stack
|
||||
// (assuming we only support stacks that grow down), so there's no danger
|
||||
// of them being merged with other stack regions. At the top, there's no
|
||||
// protection page, and no way to allocate one without using pthreads
|
||||
// directly and allocating our own stacks. So we get around the problem by
|
||||
// adding an extra VM flag (NOHUGEPAGES) to our stack region, which we
|
||||
// don't expect to be set on any heap regions. But this is not fool-proof.
|
||||
//
|
||||
// A second kink is that different C libraries (and different versions
|
||||
// thereof) report stack base locations and sizes differently with regard
|
||||
// to the guard page. For the libraries that include the guard page in the
|
||||
// stack size base pointer, we need to adjust those values to compensate.
|
||||
// But it's possible that our logic will get out of sync with library
|
||||
// changes, or someone will compile with an unexpected library.
|
||||
//
|
||||
//
|
||||
// The upshot of all of this is that there may be configurations that our
|
||||
// special cases don't cover. And if there are, we want to know about it.
|
||||
// So assert that total size of the memory region we're reporting actually
|
||||
// matches the allocated size of the thread stack.
|
||||
MOZ_ASSERT(mappings[idx].Size() == thread->StackSize(),
|
||||
"Mapping region size doesn't match stack allocation size");
|
||||
|
||||
threads.AppendElement(ThreadData{
|
||||
nsCString(PR_GetThreadName(thread->GetPRThread())),
|
||||
thread->ThreadId(),
|
||||
// On Linux, it's possible (but unlikely) that our stack region will
|
||||
// have been merged with adjacent heap regions, in which case we'll get
|
||||
// combined size information for both. So we take the minimum of the
|
||||
// reported private size and the requested stack size to avoid the
|
||||
// possible of majorly over-reporting in that case.
|
||||
std::min(privateSize, thread->StackSize()),
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
nsPrintfCString path("explicit/thread-stacks/%s (tid=%u)",
|
||||
thread.mName.get(), thread.mThreadId);
|
||||
|
||||
aHandleReport->Callback(
|
||||
EmptyCString(), path,
|
||||
KIND_NONHEAP, UNITS_BYTES,
|
||||
thread.mPrivateSize,
|
||||
NS_LITERAL_CSTRING("The sizes of thread stacks which have been "
|
||||
"committed to memory."),
|
||||
aData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
NS_IMPL_ISUPPORTS(ThreadStacksReporter, nsIMemoryReporter)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// Ideally, this would be implemented in BlockingResourceBase.cpp.
|
||||
|
@ -1559,6 +1665,10 @@ nsMemoryReporterManager::Init()
|
|||
|
||||
RegisterStrongReporter(new AtomTablesReporter());
|
||||
|
||||
#ifdef XP_LINUX
|
||||
RegisterStrongReporter(new ThreadStacksReporter());
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
RegisterStrongReporter(new DeadlockDetectorReporter());
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "nsThread.h"
|
||||
|
||||
#include "base/message_loop.h"
|
||||
#include "base/platform_thread.h"
|
||||
|
||||
// Chromium's logging can sometimes leak through...
|
||||
#ifdef LOG
|
||||
|
@ -410,6 +411,7 @@ nsThread::ThreadFunc(void* aArg)
|
|||
nsThread* self = initData->thread; // strong reference
|
||||
|
||||
self->mThread = PR_GetCurrentThread();
|
||||
self->mThreadId = uint32_t(PlatformThread::CurrentId());
|
||||
self->mVirtualThread = GetCurrentVirtualThread();
|
||||
self->mEventTarget->SetCurrentThread();
|
||||
SetupCurrentThreadForChaosMode();
|
||||
|
|
|
@ -74,6 +74,8 @@ public:
|
|||
const void* StackBase() const { return mStackBase; }
|
||||
size_t StackSize() const { return mStackSize; }
|
||||
|
||||
uint32_t ThreadId() const { return mThreadId; }
|
||||
|
||||
// If this flag is true, then the nsThread was created using
|
||||
// nsIThreadManager::NewThread.
|
||||
bool ShutdownRequired()
|
||||
|
@ -177,6 +179,7 @@ protected:
|
|||
nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2> mEventObservers;
|
||||
|
||||
int32_t mPriority;
|
||||
uint32_t mThreadId;
|
||||
PRThread* mThread;
|
||||
uint32_t mNestedEventLoopDepth;
|
||||
uint32_t mStackSize;
|
||||
|
|
Загрузка…
Ссылка в новой задаче