Bug 1475899: Part 5 - Add thread stack memory reporter for Windows. r=erahm,aklotz

MozReview-Commit-ID: Bv6OPmUhl5Y

--HG--
extra : rebase_source : b159db438a3c1a7b9d2bbb11f36100f9a89d567e
This commit is contained in:
Kris Maglione 2018-07-17 13:09:45 -07:00
parent 3d58783354
commit 4dab7b4343
5 changed files with 227 additions and 5 deletions

105
xpcom/base/MemoryInfo.cpp Normal file
View File

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/MemoryInfo.h"
#include "mozilla/DebugOnly.h"
#include <WinBase.h>
namespace mozilla {
/* static */ MemoryInfo
MemoryInfo::Get(const void* aPtr, size_t aSize)
{
MemoryInfo result;
result.mStart = uintptr_t(aPtr);
const char* ptr = reinterpret_cast<const char*>(aPtr);
const char* end = ptr + aSize;
DebugOnly<void*> base = nullptr;
while (ptr < end) {
MEMORY_BASIC_INFORMATION basicInfo;
if (!VirtualQuery(ptr, &basicInfo, sizeof(basicInfo))) {
break;
}
MOZ_ASSERT_IF(base, base == basicInfo.AllocationBase);
base = basicInfo.AllocationBase;
size_t regionSize = std::min(size_t(basicInfo.RegionSize),
size_t(end - ptr));
if (basicInfo.State == MEM_COMMIT) {
result.mCommitted += regionSize;
} else if (basicInfo.State == MEM_RESERVE) {
result.mReserved += regionSize;
} else if (basicInfo.State == MEM_FREE) {
result.mFree += regionSize;
} else {
MOZ_ASSERT_UNREACHABLE("Unexpected region state");
}
result.mSize += regionSize;
ptr += regionSize;
if (result.mType.isEmpty()) {
if (basicInfo.Type & MEM_IMAGE) {
result.mType += PageType::Image;
}
if (basicInfo.Type & MEM_MAPPED) {
result.mType += PageType::Mapped;
}
if (basicInfo.Type & MEM_PRIVATE) {
result.mType += PageType::Private;
}
// The first 8 bits of AllocationProtect are an enum. The remaining bits
// are flags.
switch (basicInfo.AllocationProtect & 0xff) {
case PAGE_EXECUTE_WRITECOPY:
result.mPerms += Perm::CopyOnWrite;
MOZ_FALLTHROUGH;
case PAGE_EXECUTE_READWRITE:
result.mPerms += Perm::Write;
MOZ_FALLTHROUGH;
case PAGE_EXECUTE_READ:
result.mPerms += Perm::Read;
MOZ_FALLTHROUGH;
case PAGE_EXECUTE:
result.mPerms += Perm::Execute;
break;
case PAGE_WRITECOPY:
result.mPerms += Perm::CopyOnWrite;
MOZ_FALLTHROUGH;
case PAGE_READWRITE:
result.mPerms += Perm::Write;
MOZ_FALLTHROUGH;
case PAGE_READONLY:
result.mPerms += Perm::Read;
break;
default:
break;
}
if (basicInfo.AllocationProtect & PAGE_GUARD) {
result.mPerms += Perm::Guard;
}
if (basicInfo.AllocationProtect & PAGE_NOCACHE) {
result.mPerms += Perm::NoCache;
}
if (basicInfo.AllocationProtect & PAGE_WRITECOMBINE) {
result.mPerms += Perm::WriteCombine;
}
}
}
result.mEnd = uintptr_t(ptr);
return result;
}
} // namespace mozilla

83
xpcom/base/MemoryInfo.h Normal file
View File

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_MemoryInfo_h
#define mozilla_MemoryInfo_h
#include "mozilla/EnumSet.h"
#include "nsTArray.h"
/**
* MemoryInfo is a helper class which describes the attributes and sizes of a
* particular region of VM memory on Windows. It roughtly corresponds to the
* values in a MEMORY_BASIC_INFORMATION struct, summed over an entire region or
* memory.
*/
namespace mozilla {
class MemoryInfo final
{
public:
enum class Perm : uint8_t
{
Read,
Write,
Execute,
CopyOnWrite,
Guard,
NoCache,
WriteCombine,
};
enum class PageType : uint8_t
{
Image,
Mapped,
Private,
};
using PermSet = EnumSet<Perm>;
using PageTypeSet = EnumSet<PageType>;
MemoryInfo() = default;
MOZ_IMPLICIT MemoryInfo(const MemoryInfo&) = default;
uintptr_t Start() const { return mStart; }
uintptr_t End() const { return mEnd; }
PageTypeSet Type() const { return mType; }
PermSet Perms() const { return mPerms; }
size_t Reserved() const { return mReserved; }
size_t Committed() const { return mCommitted; }
size_t Free() const { return mFree; }
size_t Size() const { return mSize; }
// Returns a MemoryInfo object containing the sums of all region sizes,
// divided into Reserved, Committed, and Free, depending on their State
// properties.
//
// The entire range of aSize bytes starting at aPtr must correspond to a
// single allocation. This restriction is enforced in debug builds.
static MemoryInfo Get(const void* aPtr, size_t aSize);
private:
uintptr_t mStart = 0;
uintptr_t mEnd = 0;
size_t mReserved = 0;
size_t mCommitted = 0;
size_t mFree = 0;
size_t mSize = 0;
PageTypeSet mType{};
PermSet mPerms{};
};
} // namespace mozilla
#endif // mozilla_MemoryInfo_h

View File

@ -112,6 +112,7 @@ EXPORTS.mozilla += [
'IntentionalCrash.h',
'JSObjectHolder.h',
'Logging.h',
'MemoryInfo.h',
'MemoryMapping.h',
'MemoryReportingProcess.h',
'nsMemoryInfoDumper.h',
@ -179,6 +180,11 @@ if CONFIG['OS_TARGET'] in ('Linux', 'Android'):
'MemoryMapping.cpp',
]
if CONFIG['OS_TARGET'] == 'WINNT':
UNIFIED_SOURCES += [
'MemoryInfo.cpp',
]
GENERATED_FILES += [
"error_list.rs",
"ErrorList.h",

View File

@ -25,6 +25,7 @@
#include "nsMemoryInfoDumper.h"
#endif
#include "nsNetCID.h"
#include "nsThread.h"
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReportingProcess.h"
#include "mozilla/PodOperations.h"
@ -39,6 +40,8 @@
#include "mozilla/ipc/FileDescriptorUtils.h"
#ifdef XP_WIN
#include "mozilla/MemoryInfo.h"
#include <process.h>
#ifndef getpid
#define getpid _getpid
@ -58,7 +61,6 @@ using namespace dom;
#if defined(XP_LINUX)
#include "mozilla/MemoryMapping.h"
#include "nsThread.h"
#include <malloc.h>
#include <string.h>
@ -1402,7 +1404,7 @@ public:
};
NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
#ifdef XP_LINUX
#if defined(XP_LINUX) || defined(XP_WIN)
class ThreadStacksReporter final : public nsIMemoryReporter
{
~ThreadStacksReporter() = default;
@ -1413,8 +1415,10 @@ public:
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override
{
#ifdef XP_LINUX
nsTArray<MemoryMapping> mappings(1024);
MOZ_TRY(GetMemoryMappings(mappings));
#endif
// Enumerating over active threads requires holding a lock, so we collect
// info on all threads, and then call our reporter callbacks after releasing
@ -1432,6 +1436,7 @@ public:
continue;
}
#ifdef XP_LINUX
int idx = mappings.BinaryIndexOf(thread->StackBase());
if (idx < 0) {
continue;
@ -1472,6 +1477,10 @@ public:
// matches the allocated size of the thread stack.
MOZ_ASSERT(mappings[idx].Size() == thread->StackSize(),
"Mapping region size doesn't match stack allocation size");
#else
auto memInfo = MemoryInfo::Get(thread->StackBase(), thread->StackSize());
size_t privateSize = memInfo.Committed();
#endif
threads.AppendElement(ThreadData{
nsCString(PR_GetThreadName(thread->GetPRThread())),
@ -1665,7 +1674,7 @@ nsMemoryReporterManager::Init()
RegisterStrongReporter(new AtomTablesReporter());
#ifdef XP_LINUX
#if defined(XP_LINUX) || defined(XP_WIN)
RegisterStrongReporter(new ThreadStacksReporter());
#endif

View File

@ -61,6 +61,15 @@
#include <stdio.h>
#endif
#ifdef XP_WIN
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
#include <Winbase.h>
using GetCurrentThreadStackLimitsFn = void (WINAPI*)(
PULONG_PTR LowLimit, PULONG_PTR HighLimit);
#endif
#define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
_XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
!(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
@ -420,8 +429,8 @@ nsThread::ThreadFunc(void* aArg)
NS_SetCurrentThreadName(initData->name.BeginReading());
}
#ifdef XP_LINUX
{
#if defined(XP_LINUX)
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_getattr_np(pthread_self(), &attr);
@ -473,8 +482,18 @@ nsThread::ThreadFunc(void* aArg)
madvise(self->mStackBase, stackSize, MADV_NOHUGEPAGE);
pthread_attr_destroy(&attr);
}
#elif defined(XP_WIN)
static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
if (sGetStackLimits) {
ULONG_PTR stackBottom, stackTop;
sGetStackLimits(&stackBottom, &stackTop);
self->mStackBase = reinterpret_cast<void*>(stackBottom);
self->mStackSize = stackTop - stackBottom;
}
#endif
}
// Inform the ThreadManager
nsThreadManager::get().RegisterCurrentThread(*self);