Bug 820652 (part 3) - DMD: Distinguish BlockSize and GroupSize. r=jlebar.

--HG--
extra : rebase_source : de31af9d5c00e52a7f775d7e0e11204edcb9e21b
This commit is contained in:
Nicholas Nethercote 2012-12-11 19:47:46 -08:00
parent 784df47c48
commit 67b10f4d22

View File

@ -149,6 +149,13 @@ public:
static void reportAllocOverflow() { ExitOnFailure(nullptr); }
};
// This is only needed because of the |const void*| vs |void*| arg mismatch.
static size_t
MallocSizeOf(const void* aPtr)
{
return gMallocTable->malloc_usable_size(const_cast<void*>(aPtr));
}
static void
StatusMsg(const char* aFmt, ...)
{
@ -778,46 +785,33 @@ public:
class BlockSize
{
static const size_t kSlopBits = sizeof(size_t) * 8 - 1; // 31 or 63
static const size_t kReqBits = sizeof(size_t) * 8 - 1; // 31 or 63
// This assumes that we'll never request an allocation of 2 GiB or more on
// 32-bit platforms.
const size_t mReq:kReqBits; // size requested
const size_t mSampled:1; // was this block sampled? (if so, slop == 0)
public:
size_t mReq; // size requested
size_t mSlop:kSlopBits; // additional bytes allocated due to rounding up
size_t mSampled:1; // were one or more blocks contributing to this
// BlockSize sampled?
BlockSize()
: mReq(0),
mSlop(0),
mSampled(false)
{}
BlockSize(size_t aReq, size_t aSlop, bool aSampled)
BlockSize(size_t aReq, bool aSampled)
: mReq(aReq),
mSlop(aSlop),
mSampled(aSampled)
{}
size_t Usable() const { return mReq + mSlop; }
size_t Req() const { return mReq; }
void Add(const BlockSize& aBlockSize)
// Sampled blocks always have zero slop.
size_t Slop(const void* aPtr) const
{
mReq += aBlockSize.mReq;
mSlop += aBlockSize.mSlop;
mSampled = mSampled || aBlockSize.mSampled;
return mSampled ? 0 : MallocSizeOf(aPtr) - mReq;
}
static int Cmp(const BlockSize& aA, const BlockSize& aB)
size_t Usable(const void* aPtr) const
{
// Primary sort: put bigger usable sizes before smaller usable sizes.
if (aA.Usable() > aB.Usable()) return -1;
if (aA.Usable() < aB.Usable()) return 1;
// Secondary sort: put non-sampled groups before sampled groups.
if (!aA.mSampled && aB.mSampled) return -1;
if ( aA.mSampled && !aB.mSampled) return 1;
return 0;
return mSampled ? mReq : MallocSizeOf(aPtr);
}
bool IsSampled() const { return mSampled; }
};
// A live heap block.
@ -827,13 +821,13 @@ public:
const BlockSize mBlockSize;
public:
LiveBlock(size_t aReqSize, size_t aSlopSize,
const StackTrace* aAllocStackTrace, bool aIsExact)
LiveBlock(size_t aReqSize, const StackTrace* aAllocStackTrace, bool aSampled)
: LiveBlockKey(aAllocStackTrace),
mBlockSize(aReqSize, aSlopSize, aIsExact)
mBlockSize(aReqSize, aSampled)
{}
void Report(Thread* aT, const char* aReporterName, bool aReportedOnAlloc);
void Report(Thread* aT, const void* aPtr, const char* aReporterName,
bool aReportedOnAlloc);
void UnreportIfNotReportedOnAlloc();
};
@ -863,7 +857,6 @@ AllocCallback(void* aPtr, size_t aReqSize, Thread* aT)
AutoBlockIntercepts block(aT);
size_t actualSize = gMallocTable->malloc_usable_size(aPtr);
size_t slopSize = actualSize - aReqSize;
if (actualSize < gSampleBelowSize) {
// If this allocation is smaller than the sample-below size, increment the
@ -874,13 +867,12 @@ AllocCallback(void* aPtr, size_t aReqSize, Thread* aT)
if (gSmallBlockActualSizeCounter >= gSampleBelowSize) {
gSmallBlockActualSizeCounter -= gSampleBelowSize;
LiveBlock b(gSampleBelowSize, /* slopSize */ 0, StackTrace::Get(aT),
/* sampled */ true);
LiveBlock b(gSampleBelowSize, StackTrace::Get(aT), /* sampled */ true);
(void)gLiveBlockTable->putNew(aPtr, b);
}
} else {
// If this block size is larger than the sample size, record it exactly.
LiveBlock b(aReqSize, slopSize, StackTrace::Get(aT), /* sampled */ false);
LiveBlock b(aReqSize, StackTrace::Get(aT), /* sampled */ false);
(void)gLiveBlockTable->putNew(aPtr, b);
}
}
@ -1045,6 +1037,55 @@ namespace dmd {
// Live and double-report block groups
//---------------------------------------------------------------------------
class GroupSize
{
static const size_t kReqBits = sizeof(size_t) * 8 - 1; // 31 or 63
size_t mReq; // size requested
size_t mSlop:kReqBits; // slop bytes
size_t mSampled:1; // were one or more blocks contributing to this
// GroupSize sampled?
public:
GroupSize()
: mReq(0),
mSlop(0),
mSampled(false)
{}
size_t Req() const { return mReq; }
size_t Slop() const { return mSlop; }
size_t Usable() const { return mReq + mSlop; }
bool IsSampled() const { return mSampled; }
void Add(const void* aPtr, const BlockSize& aBlockSize)
{
mReq += aBlockSize.Req();
mSlop += aBlockSize.Slop(aPtr);
mSampled = mSampled || aBlockSize.IsSampled();
}
void Add(const GroupSize& aGroupSize)
{
mReq += aGroupSize.Req();
mSlop += aGroupSize.Slop();
mSampled = mSampled || aGroupSize.IsSampled();
}
static int Cmp(const GroupSize& aA, const GroupSize& aB)
{
// Primary sort: put bigger usable sizes before smaller usable sizes.
if (aA.Usable() > aB.Usable()) return -1;
if (aA.Usable() < aB.Usable()) return 1;
// Secondary sort: put non-sampled groups before sampled groups.
if (!aA.mSampled && aB.mSampled) return -1;
if ( aA.mSampled && !aB.mSampled) return 1;
return 0;
}
};
class BlockGroup
{
protected:
@ -1052,22 +1093,22 @@ protected:
// {Live,DoubleReport}BlockGroupTable. Thes two fields constitute the value,
// so it's ok for them to be |mutable|.
mutable uint32_t mNumBlocks; // number of blocks with this LiveBlockKey
mutable BlockSize mCombinedSize; // combined size of those blocks
mutable GroupSize mGroupSize; // combined size of those blocks
public:
BlockGroup()
: mNumBlocks(0),
mCombinedSize()
mGroupSize()
{}
const BlockSize& CombinedSize() const { return mCombinedSize; }
const GroupSize& GroupSize() const { return mGroupSize; }
// The |const| qualifier is something of a lie, but is necessary so this type
// can be used in js::HashSet, and it fits with the |mutable| fields above.
void Add(const LiveBlock& aB) const
void Add(const void* aPtr, const LiveBlock& aB) const
{
mNumBlocks++;
mCombinedSize.Add(aB.mBlockSize);
mGroupSize.Add(aPtr, aB.mBlockSize);
}
static const char* const kName; // for PrintSortedGroups
@ -1098,7 +1139,7 @@ public:
const LiveBlockGroup* const b =
*static_cast<const LiveBlockGroup* const*>(aB);
return BlockSize::Cmp(a->mCombinedSize, b->mCombinedSize);
return GroupSize::Cmp(a->mGroupSize, b->mGroupSize);
}
};
@ -1111,7 +1152,7 @@ LiveBlockGroup::Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
size_t aCategoryUsableSize, size_t aCumulativeUsableSize,
size_t aTotalUsableSize) const
{
bool showTilde = mCombinedSize.mSampled;
bool showTilde = mGroupSize.IsSampled();
W("%s: %s block%s in block group %s of %s\n",
aStr,
@ -1120,15 +1161,15 @@ LiveBlockGroup::Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
Show(aN, gBuf3, kBufLen));
W(" %s bytes (%s requested / %s slop)\n",
Show(mCombinedSize.Usable(), gBuf1, kBufLen, showTilde),
Show(mCombinedSize.mReq, gBuf2, kBufLen, showTilde),
Show(mCombinedSize.mSlop, gBuf3, kBufLen, showTilde));
Show(mGroupSize.Usable(), gBuf1, kBufLen, showTilde),
Show(mGroupSize.Req(), gBuf2, kBufLen, showTilde),
Show(mGroupSize.Slop(), gBuf3, kBufLen, showTilde));
W(" %4.2f%% of the heap (%4.2f%% cumulative); "
" %4.2f%% of %s (%4.2f%% cumulative)\n",
Percent(mCombinedSize.Usable(), aTotalUsableSize),
Percent(mGroupSize.Usable(), aTotalUsableSize),
Percent(aCumulativeUsableSize, aTotalUsableSize),
Percent(mCombinedSize.Usable(), aCategoryUsableSize),
Percent(mGroupSize.Usable(), aCategoryUsableSize),
astr,
Percent(aCumulativeUsableSize, aCategoryUsableSize));
@ -1165,7 +1206,7 @@ public:
const DoubleReportBlockGroup* const b =
*static_cast<const DoubleReportBlockGroup* const*>(aB);
return BlockSize::Cmp(a->mCombinedSize, b->mCombinedSize);
return GroupSize::Cmp(a->mGroupSize, b->mGroupSize);
}
};
@ -1180,7 +1221,7 @@ DoubleReportBlockGroup::Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
size_t aCumulativeUsableSize,
size_t aTotalUsableSize) const
{
bool showTilde = mCombinedSize.mSampled;
bool showTilde = mGroupSize.IsSampled();
W("%s: %s block%s in block group %s of %s\n",
aStr,
@ -1189,9 +1230,9 @@ DoubleReportBlockGroup::Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
Show(aN, gBuf3, kBufLen));
W(" %s bytes (%s requested / %s slop)\n",
Show(mCombinedSize.Usable(), gBuf1, kBufLen, showTilde),
Show(mCombinedSize.mReq, gBuf2, kBufLen, showTilde),
Show(mCombinedSize.mSlop, gBuf3, kBufLen, showTilde));
Show(mGroupSize.Usable(), gBuf1, kBufLen, showTilde),
Show(mGroupSize.Req(), gBuf2, kBufLen, showTilde),
Show(mGroupSize.Slop(), gBuf3, kBufLen, showTilde));
W(" Allocated at\n");
mAllocStackTrace->Print(aWriter);
@ -1218,17 +1259,17 @@ class FrameGroup
const void* const mPc;
mutable size_t mNumBlocks;
mutable size_t mNumBlockGroups;
mutable BlockSize mCombinedSize;
mutable GroupSize mGroupSize;
public:
explicit FrameGroup(const void* aPc)
: mPc(aPc),
mNumBlocks(0),
mNumBlockGroups(0),
mCombinedSize()
mGroupSize()
{}
const BlockSize& CombinedSize() const { return mCombinedSize; }
const GroupSize& GroupSize() const { return mGroupSize; }
// The |const| qualifier is something of a lie, but is necessary so this type
// can be used in js::HashSet, and it fits with the |mutable| fields above.
@ -1236,7 +1277,7 @@ public:
{
mNumBlocks += aBg.mNumBlocks;
mNumBlockGroups++;
mCombinedSize.Add(aBg.mCombinedSize);
mGroupSize.Add(aBg.mGroupSize);
}
void Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
@ -1249,7 +1290,7 @@ public:
const FrameGroup* const a = *static_cast<const FrameGroup* const*>(aA);
const FrameGroup* const b = *static_cast<const FrameGroup* const*>(aB);
return BlockSize::Cmp(a->mCombinedSize, b->mCombinedSize);
return GroupSize::Cmp(a->mGroupSize, b->mGroupSize);
}
static const char* const kName; // for PrintSortedGroups
@ -1282,7 +1323,7 @@ FrameGroup::Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
{
(void)aCumulativeUsableSize;
bool showTilde = mCombinedSize.mSampled;
bool showTilde = mGroupSize.IsSampled();
nsCodeAddressDetails details;
PcInfo(mPc, &details);
@ -1295,13 +1336,13 @@ FrameGroup::Print(const Writer& aWriter, uint32_t aM, uint32_t aN,
Show(aN, gBuf4, kBufLen));
W(" %s bytes (%s requested / %s slop)\n",
Show(mCombinedSize.Usable(), gBuf1, kBufLen, showTilde),
Show(mCombinedSize.mReq, gBuf2, kBufLen, showTilde),
Show(mCombinedSize.mSlop, gBuf3, kBufLen, showTilde));
Show(mGroupSize.Usable(), gBuf1, kBufLen, showTilde),
Show(mGroupSize.Req(), gBuf2, kBufLen, showTilde),
Show(mGroupSize.Slop(), gBuf3, kBufLen, showTilde));
W(" %4.2f%% of the heap; %4.2f%% of %s\n",
Percent(mCombinedSize.Usable(), aTotalUsableSize),
Percent(mCombinedSize.Usable(), aCategoryUsableSize),
Percent(mGroupSize.Usable(), aTotalUsableSize),
Percent(mGroupSize.Usable(), aCategoryUsableSize),
astr);
W(" PC is\n");
@ -1530,7 +1571,8 @@ Init(const malloc_table_t* aMallocTable)
//---------------------------------------------------------------------------
void
LiveBlock::Report(Thread* aT, const char* aReporterName, bool aOnAlloc)
LiveBlock::Report(Thread* aT, const void* aPtr, const char* aReporterName,
bool aOnAlloc)
{
if (IsReported()) {
DoubleReportBlockKey doubleReportKey(mAllocStackTrace,
@ -1542,7 +1584,7 @@ LiveBlock::Report(Thread* aT, const char* aReporterName, bool aOnAlloc)
DoubleReportBlockGroup bg(doubleReportKey);
(void)gDoubleReportBlockGroupTable->add(p, bg);
}
p->Add(*this);
p->Add(aPtr, *this);
} else {
mReporterName = aReporterName;
@ -1573,7 +1615,7 @@ ReportHelper(const void* aPtr, const char* aReporterName, bool aOnAlloc)
AutoLockState lock;
if (LiveBlockTable::Ptr p = gLiveBlockTable->lookup(aPtr)) {
p->value.Report(t, aReporterName, aOnAlloc);
p->value.Report(t, aPtr, aReporterName, aOnAlloc);
} else {
// We have no record of the block. Do nothing. Either:
// - We're sampling and we skipped this block. This is likely.
@ -1637,7 +1679,7 @@ PrintSortedGroups(const Writer& aWriter, const char* aStr, const char* astr,
size_t cumulativeUsableSize = 0;
for (uint32_t i = 0; i < numTGroups; i++) {
const TGroup* tg = tgArray[i];
cumulativeUsableSize += tg->CombinedSize().Usable();
cumulativeUsableSize += tg->GroupSize().Usable();
if (i < MaxTGroups) {
tg->Print(aWriter, i+1, numTGroups, aStr, astr, aCategoryUsableSize,
cumulativeUsableSize, aTotalUsableSize);
@ -1699,13 +1741,6 @@ PrintSortedBlockAndFrameGroups(const Writer& aWriter,
aTotalUsableSize);
}
// This is only needed because of the |const void*| vs |void*| arg mismatch.
static size_t
MallocSizeOf(const void* aPtr)
{
return gMallocTable->malloc_usable_size(const_cast<void*>(aPtr));
}
// Note that, unlike most SizeOf* functions, this function does not take a
// |nsMallocSizeOfFun| argument. That's because those arguments are primarily
// to aid DMD track heap blocks... but DMD deliberately doesn't track heap
@ -1777,10 +1812,11 @@ Dump(Writer aWriter)
for (LiveBlockTable::Range r = gLiveBlockTable->all();
!r.empty();
r.popFront()) {
const void* pc = r.front().key;
const LiveBlock& b = r.front().value;
size_t& size = !b.IsReported() ? unreportedUsableSize : reportedUsableSize;
size += b.mBlockSize.Usable();
size += b.mBlockSize.Usable(pc);
LiveBlockGroupTable& table = !b.IsReported()
? unreportedLiveBlockGroupTable
@ -1790,9 +1826,9 @@ Dump(Writer aWriter)
LiveBlockGroup bg(b);
(void)table.add(p, bg);
}
p->Add(b);
p->Add(pc, b);
anyBlocksSampled = anyBlocksSampled || b.mBlockSize.mSampled;
anyBlocksSampled = anyBlocksSampled || b.mBlockSize.IsSampled();
}
size_t totalUsableSize = unreportedUsableSize + reportedUsableSize;