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:
Kris Maglione 2018-07-14 02:21:30 -07:00
Родитель fb98e4016d
Коммит 1dbd559f77
3 изменённых файлов: 115 добавлений и 0 удалений

Просмотреть файл

@ -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;