Bug 1249503 - Make WindowsAddressSpaceReporter bloat memory reports less. r=erahm.

The WindowsAddressSpaceReporter generates one memory report per segment, and
there can be 10,000+ segments.

This patch changes things so that one memory report is generated per segment
*kind* -- at most a couple of dozen -- rather than one per *segment*.
This commit is contained in:
Nicholas Nethercote 2016-02-23 18:39:24 +11:00
parent b6e7cbf7ba
commit 687a6f5be1

View File

@ -746,6 +746,58 @@ SystemHeapSize(int64_t* aSizeOut)
return NS_OK;
}
struct SegmentKind
{
DWORD mState;
DWORD mType;
DWORD mProtect;
int mIsStack;
};
struct SegmentEntry : public PLDHashEntryHdr
{
static PLDHashNumber HashKey(PLDHashTable* aTable, const void* aKey)
{
auto kind = static_cast<const SegmentKind*>(aKey);
return mozilla::HashGeneric(kind->mState, kind->mType, kind->mProtect,
kind->mIsStack);
}
static bool MatchEntry(PLDHashTable* aTable,
const PLDHashEntryHdr* aEntry, const void* aKey)
{
auto kind = static_cast<const SegmentKind*>(aKey);
auto entry = static_cast<const SegmentEntry*>(aEntry);
return kind->mState == entry->mKind.mState &&
kind->mType == entry->mKind.mType &&
kind->mProtect == entry->mKind.mProtect &&
kind->mIsStack == entry->mKind.mIsStack;
}
static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
{
auto kind = static_cast<const SegmentKind*>(aKey);
auto entry = static_cast<SegmentEntry*>(aEntry);
entry->mKind = *kind;
entry->mCount = 0;
entry->mSize = 0;
}
static const PLDHashTableOps Ops;
SegmentKind mKind; // The segment kind.
uint32_t mCount; // The number of segments of this kind.
size_t mSize; // The combined size of segments of this kind.
};
/* static */ const PLDHashTableOps SegmentEntry::Ops = {
SegmentEntry::HashKey,
SegmentEntry::MatchEntry,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub,
SegmentEntry::InitEntry
};
class WindowsAddressSpaceReporter final : public nsIMemoryReporter
{
~WindowsAddressSpaceReporter() {}
@ -756,6 +808,11 @@ public:
NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override
{
// First iterate over all the segments and record how many of each kind
// there were and their aggregate sizes. We use a hash table for this
// because there are a couple of dozen different kinds possible.
PLDHashTable table(&SegmentEntry::Ops, sizeof(SegmentEntry));
MEMORY_BASIC_INFORMATION info = { 0 };
bool isPrevSegStackGuard = false;
for (size_t currentAddress = 0; ; ) {
@ -766,6 +823,41 @@ public:
size_t size = info.RegionSize;
// Note that |type| and |protect| are ignored in some cases.
DWORD state = info.State;
DWORD type =
(state == MEM_RESERVE || state == MEM_COMMIT) ? info.Type : 0;
DWORD protect = (state == MEM_COMMIT) ? info.Protect : 0;
bool isStack = isPrevSegStackGuard &&
state == MEM_COMMIT &&
type == MEM_PRIVATE &&
protect == PAGE_READWRITE;
SegmentKind kind = { state, type, protect, isStack ? 1 : 0 };
auto entry =
static_cast<SegmentEntry*>(table.Add(&kind, mozilla::fallible));
if (entry) {
entry->mCount += 1;
entry->mSize += size;
}
isPrevSegStackGuard = info.State == MEM_COMMIT &&
info.Type == MEM_PRIVATE &&
info.Protect == (PAGE_READWRITE|PAGE_GUARD);
size_t lastAddress = currentAddress;
currentAddress += size;
// If we overflow, we've examined all of the address space.
if (currentAddress < lastAddress) {
break;
}
}
// Then iterate over the hash table and report the details for each segment
// kind.
for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
// For each range of pages, we consider one or more of its State, Type
// and Protect values. These are documented at
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366775%28v=vs.85%29.aspx
@ -777,9 +869,11 @@ public:
bool doType = false;
bool doProtect = false;
auto entry = static_cast<const SegmentEntry*>(iter.Get());
nsCString path("address-space");
switch (info.State) {
switch (entry->mKind.mState) {
case MEM_FREE:
path.AppendLiteral("/free");
break;
@ -802,7 +896,7 @@ public:
}
if (doType) {
switch (info.Type) {
switch (entry->mKind.mType) {
case MEM_IMAGE:
path.AppendLiteral("/image");
break;
@ -823,72 +917,60 @@ public:
}
if (doProtect) {
DWORD protect = entry->mKind.mProtect;
// Basic attributes. Exactly one of these should be set.
if (info.Protect & PAGE_EXECUTE) {
if (protect & PAGE_EXECUTE) {
path.AppendLiteral("/execute");
}
if (info.Protect & PAGE_EXECUTE_READ) {
if (protect & PAGE_EXECUTE_READ) {
path.AppendLiteral("/execute-read");
}
if (info.Protect & PAGE_EXECUTE_READWRITE) {
if (protect & PAGE_EXECUTE_READWRITE) {
path.AppendLiteral("/execute-readwrite");
}
if (info.Protect & PAGE_EXECUTE_WRITECOPY) {
if (protect & PAGE_EXECUTE_WRITECOPY) {
path.AppendLiteral("/execute-writecopy");
}
if (info.Protect & PAGE_NOACCESS) {
if (protect & PAGE_NOACCESS) {
path.AppendLiteral("/noaccess");
}
if (info.Protect & PAGE_READONLY) {
if (protect & PAGE_READONLY) {
path.AppendLiteral("/readonly");
}
if (info.Protect & PAGE_READWRITE) {
if (protect & PAGE_READWRITE) {
path.AppendLiteral("/readwrite");
}
if (info.Protect & PAGE_WRITECOPY) {
if (protect & PAGE_WRITECOPY) {
path.AppendLiteral("/writecopy");
}
// Modifiers. At most one of these should be set.
if (info.Protect & PAGE_GUARD) {
if (protect & PAGE_GUARD) {
path.AppendLiteral("+guard");
}
if (info.Protect & PAGE_NOCACHE) {
if (protect & PAGE_NOCACHE) {
path.AppendLiteral("+nocache");
}
if (info.Protect & PAGE_WRITECOMBINE) {
if (protect & PAGE_WRITECOMBINE) {
path.AppendLiteral("+writecombine");
}
// Annotate likely stack segments, too.
if (isPrevSegStackGuard &&
info.State == MEM_COMMIT &&
doType && info.Type == MEM_PRIVATE &&
doProtect && info.Protect == PAGE_READWRITE) {
path.AppendLiteral(" (stack)");
if (entry->mKind.mIsStack) {
path.AppendLiteral("+stack");
}
}
isPrevSegStackGuard =
info.State == MEM_COMMIT &&
doType && info.Type == MEM_PRIVATE &&
doProtect && info.Protect == (PAGE_READWRITE|PAGE_GUARD);
// Append the segment count.
path.AppendPrintf("(segments=%u)", entry->mCount);
nsresult rv;
rv = aHandleReport->Callback(
EmptyCString(), path, KIND_OTHER, UNITS_BYTES, size,
EmptyCString(), path, KIND_OTHER, UNITS_BYTES, entry->mSize,
NS_LITERAL_CSTRING("From MEMORY_BASIC_INFORMATION."), aData);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
size_t lastAddress = currentAddress;
currentAddress += size;
// If we overflow, we've examined all of the address space.
if (currentAddress < lastAddress) {
break;
}
}
return NS_OK;