mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
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:
parent
3d58783354
commit
4dab7b4343
105
xpcom/base/MemoryInfo.cpp
Normal file
105
xpcom/base/MemoryInfo.cpp
Normal 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
83
xpcom/base/MemoryInfo.h
Normal 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
|
@ -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",
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user