mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1094552 (part 6) - DMD: add support for cumulative heap profiling. r=mccr8.
By adding a new "cumulative" mode. --HG-- extra : rebase_source : 5c851b7c594a134fae48393ff0becfd057715041
This commit is contained in:
parent
5b5d3c27bb
commit
5cc6a39a9a
@ -38,6 +38,7 @@
|
||||
#include "mozilla/JSONWriter.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/SegmentedVector.h"
|
||||
|
||||
// CodeAddressService is defined entirely in the header, so this does not make
|
||||
// DMD depend on XPCOM's object file.
|
||||
@ -336,7 +337,12 @@ class Options
|
||||
// Like "Live", but for each live block it also outputs: zero or more
|
||||
// report stacks. This mode is good for identifying where memory reporters
|
||||
// should be added. This is the default mode.
|
||||
DarkMatter
|
||||
DarkMatter,
|
||||
|
||||
// Like "Live", but also outputs the same data for dead blocks. This mode
|
||||
// does cumulative heap profiling, which is good for identifying where large
|
||||
// amounts of short-lived allocations occur.
|
||||
Cumulative
|
||||
};
|
||||
|
||||
char* mDMDEnvVar; // a saved copy, for later printing
|
||||
@ -357,6 +363,7 @@ public:
|
||||
|
||||
bool IsLiveMode() const { return mMode == Live; }
|
||||
bool IsDarkMatterMode() const { return mMode == DarkMatter; }
|
||||
bool IsCumulativeMode() const { return mMode == Cumulative; }
|
||||
|
||||
const char* DMDEnvVar() const { return mDMDEnvVar; }
|
||||
|
||||
@ -438,10 +445,10 @@ public:
|
||||
};
|
||||
|
||||
// This lock must be held while manipulating global state such as
|
||||
// gStackTraceTable, gLiveBlockTable, etc. Note that gOptions is *not*
|
||||
// protected by this lock because it is only written to by Options(), which is
|
||||
// only invoked at start-up and in ResetEverything(), which is only used by
|
||||
// SmokeDMD.cpp.
|
||||
// gStackTraceTable, gLiveBlockTable, gDeadBlockList. Note that gOptions is
|
||||
// *not* protected by this lock because it is only written to by Options(),
|
||||
// which is only invoked at start-up and in ResetEverything(), which is only
|
||||
// used by SmokeDMD.cpp.
|
||||
static Mutex* gStateLock = nullptr;
|
||||
|
||||
class AutoLockState
|
||||
@ -850,10 +857,10 @@ class LiveBlock
|
||||
public:
|
||||
LiveBlock(const void* aPtr, size_t aReqSize,
|
||||
const StackTrace* aAllocStackTrace, bool aIsSampled)
|
||||
: mPtr(aPtr),
|
||||
mReqSize(aReqSize),
|
||||
mAllocStackTrace_mIsSampled(aAllocStackTrace, aIsSampled),
|
||||
mReportStackTrace_mReportedOnAlloc() // all fields get zeroed
|
||||
: mPtr(aPtr)
|
||||
, mReqSize(aReqSize)
|
||||
, mAllocStackTrace_mIsSampled(aAllocStackTrace, aIsSampled)
|
||||
, mReportStackTrace_mReportedOnAlloc() // all fields get zeroed
|
||||
{
|
||||
MOZ_ASSERT(aAllocStackTrace);
|
||||
}
|
||||
@ -982,6 +989,60 @@ public:
|
||||
typedef js::HashSet<LiveBlock, LiveBlock, InfallibleAllocPolicy> LiveBlockTable;
|
||||
static LiveBlockTable* gLiveBlockTable = nullptr;
|
||||
|
||||
// A freed heap block.
|
||||
class DeadBlock
|
||||
{
|
||||
const size_t mReqSize; // size requested
|
||||
const size_t mSlopSize; // slop above size requested
|
||||
|
||||
// Ptr: |mAllocStackTrace| - stack trace where this block was allocated.
|
||||
// Tag bit 0: |mIsSampled| - was this block sampled? (if so, slop == 0).
|
||||
TaggedPtr<const StackTrace* const>
|
||||
mAllocStackTrace_mIsSampled;
|
||||
|
||||
public:
|
||||
DeadBlock()
|
||||
: mReqSize(0)
|
||||
, mSlopSize(0)
|
||||
, mAllocStackTrace_mIsSampled(nullptr, 0)
|
||||
{}
|
||||
|
||||
explicit DeadBlock(const LiveBlock& aLb)
|
||||
: mReqSize(aLb.ReqSize())
|
||||
, mSlopSize(aLb.SlopSize())
|
||||
, mAllocStackTrace_mIsSampled(aLb.AllocStackTrace(), aLb.IsSampled())
|
||||
{
|
||||
MOZ_ASSERT(AllocStackTrace());
|
||||
MOZ_ASSERT_IF(IsSampled(), SlopSize() == 0);
|
||||
}
|
||||
|
||||
~DeadBlock() {}
|
||||
|
||||
size_t ReqSize() const { return mReqSize; }
|
||||
size_t SlopSize() const { return mSlopSize; }
|
||||
size_t UsableSize() const { return mReqSize + mSlopSize; }
|
||||
|
||||
bool IsSampled() const
|
||||
{
|
||||
return mAllocStackTrace_mIsSampled.Tag();
|
||||
}
|
||||
|
||||
const StackTrace* AllocStackTrace() const
|
||||
{
|
||||
return mAllocStackTrace_mIsSampled.Ptr();
|
||||
}
|
||||
|
||||
void AddStackTracesToTable(StackTraceSet& aStackTraces) const
|
||||
{
|
||||
aStackTraces.put(AllocStackTrace()); // never null
|
||||
}
|
||||
};
|
||||
|
||||
static const size_t kDeadBlockListSegmentSize = 16384;
|
||||
typedef SegmentedVector<DeadBlock, kDeadBlockListSegmentSize,
|
||||
InfallibleAllocPolicy> DeadBlockList;
|
||||
static DeadBlockList* gDeadBlockList = nullptr;
|
||||
|
||||
// Add a pointer to each live stack trace into the given StackTraceSet. (A
|
||||
// stack trace is live if it's used by one of the live blocks.)
|
||||
static void
|
||||
@ -996,6 +1057,10 @@ GatherUsedStackTraces(StackTraceSet& aStackTraces)
|
||||
for (auto r = gLiveBlockTable->all(); !r.empty(); r.popFront()) {
|
||||
r.front().AddStackTracesToTable(aStackTraces);
|
||||
}
|
||||
|
||||
for (auto iter = gDeadBlockList->Iter(); !iter.Done(); iter.Next()) {
|
||||
iter.Get().AddStackTracesToTable(aStackTraces);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete stack traces that we aren't using, and compact our hashtable.
|
||||
@ -1063,7 +1128,7 @@ AllocCallback(void* aPtr, size_t aReqSize, Thread* aT)
|
||||
}
|
||||
|
||||
static void
|
||||
FreeCallback(void* aPtr, Thread* aT)
|
||||
FreeCallback(void* aPtr, Thread* aT, DeadBlock* aDeadBlock)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return;
|
||||
@ -1072,7 +1137,17 @@ FreeCallback(void* aPtr, Thread* aT)
|
||||
AutoLockState lock;
|
||||
AutoBlockIntercepts block(aT);
|
||||
|
||||
gLiveBlockTable->remove(aPtr);
|
||||
if (LiveBlockTable::Ptr lb = gLiveBlockTable->lookup(aPtr)) {
|
||||
if (gOptions->IsCumulativeMode()) {
|
||||
// Copy it out so it can be added to the dead block list later.
|
||||
new (aDeadBlock) DeadBlock(*lb);
|
||||
}
|
||||
gLiveBlockTable->remove(lb);
|
||||
} else {
|
||||
// We have no record of the block. Do nothing. Either:
|
||||
// - We're sampling and we skipped this block. This is likely.
|
||||
// - It's a bogus pointer.
|
||||
}
|
||||
|
||||
if (gStackTraceTable->count() > gGCStackTraceTableWhenSizeExceeds) {
|
||||
GCStackTraces();
|
||||
@ -1168,15 +1243,22 @@ replace_realloc(void* aOldPtr, size_t aSize)
|
||||
// the realloc to avoid races, just like in replace_free().
|
||||
// Nb: This does an unnecessary hashtable remove+add if the block doesn't
|
||||
// move, but doing better isn't worth the effort.
|
||||
FreeCallback(aOldPtr, t);
|
||||
DeadBlock db;
|
||||
FreeCallback(aOldPtr, t, &db);
|
||||
void* ptr = gMallocTable->realloc(aOldPtr, aSize);
|
||||
if (ptr) {
|
||||
AllocCallback(ptr, aSize, t);
|
||||
if (gOptions->IsCumulativeMode() && db.AllocStackTrace()) {
|
||||
AutoLockState lock;
|
||||
gDeadBlockList->InfallibleAppend(db);
|
||||
}
|
||||
} else {
|
||||
// If realloc fails, we re-insert the old pointer. It will look like it
|
||||
// was allocated for the first time here, which is untrue, and the slop
|
||||
// bytes will be zero, which may be untrue. But this case is rare and
|
||||
// doing better isn't worth the effort.
|
||||
// If realloc fails, we undo the prior operations by re-inserting the old
|
||||
// pointer into the live block table. We don't have to do anything with the
|
||||
// dead block list because the dead block hasn't yet been inserted. The
|
||||
// block will end up looking like it was allocated for the first time here,
|
||||
// which is untrue, and the slop bytes will be zero, which may be untrue.
|
||||
// But this case is rare and doing better isn't worth the effort.
|
||||
AllocCallback(aOldPtr, gMallocTable->malloc_usable_size(aOldPtr), t);
|
||||
}
|
||||
return ptr;
|
||||
@ -1219,7 +1301,12 @@ replace_free(void* aPtr)
|
||||
// Do the actual free after updating the table. Otherwise, another thread
|
||||
// could call malloc and get the freed block and update the table, and then
|
||||
// our update here would remove the newly-malloc'd block.
|
||||
FreeCallback(aPtr, t);
|
||||
DeadBlock db;
|
||||
FreeCallback(aPtr, t, &db);
|
||||
if (gOptions->IsCumulativeMode() && db.AllocStackTrace()) {
|
||||
AutoLockState lock;
|
||||
gDeadBlockList->InfallibleAppend(db);
|
||||
}
|
||||
gMallocTable->free(aPtr);
|
||||
}
|
||||
|
||||
@ -1323,6 +1410,8 @@ Options::Options(const char* aDMDEnvVar)
|
||||
mMode = Options::Live;
|
||||
} else if (strcmp(arg, "--mode=dark-matter") == 0) {
|
||||
mMode = Options::DarkMatter;
|
||||
} else if (strcmp(arg, "--mode=cumulative") == 0) {
|
||||
mMode = Options::Cumulative;
|
||||
|
||||
} else if (GetLong(arg, "--sample-below", 1, mSampleBelowSize.mMax,
|
||||
&myLong)) {
|
||||
@ -1359,7 +1448,7 @@ Options::BadArg(const char* aArg)
|
||||
StatusMsg("\n");
|
||||
StatusMsg("The following options are allowed; defaults are shown in [].\n");
|
||||
StatusMsg(" --mode=<mode> Profiling mode [dark-matter]\n");
|
||||
StatusMsg(" where <mode> is one of: live, dark-matter\n");
|
||||
StatusMsg(" where <mode> is one of: live, dark-matter, cumulative\n");
|
||||
StatusMsg(" --sample-below=<1..%d> Sample blocks smaller than this [%d]\n",
|
||||
int(mSampleBelowSize.mMax),
|
||||
int(mSampleBelowSize.mDefault));
|
||||
@ -1430,6 +1519,12 @@ Init(const malloc_table_t* aMallocTable)
|
||||
|
||||
gLiveBlockTable = InfallibleAllocPolicy::new_<LiveBlockTable>();
|
||||
gLiveBlockTable->init(8192);
|
||||
|
||||
// Create this even if the mode isn't Cumulative, in case the mode is
|
||||
// changed later on (as is done by SmokeDMD.cpp, for example). It's tiny
|
||||
// when empty, so space isn't a concern.
|
||||
gDeadBlockList =
|
||||
InfallibleAllocPolicy::new_<DeadBlockList>(kDeadBlockListSegmentSize);
|
||||
}
|
||||
|
||||
gIsDMDInitialized = true;
|
||||
@ -1516,6 +1611,8 @@ SizeOfInternal(Sizes* aSizes)
|
||||
gStackTraceTable->sizeOfIncludingThis(MallocSizeOf);
|
||||
|
||||
aSizes->mLiveBlockTable = gLiveBlockTable->sizeOfIncludingThis(MallocSizeOf);
|
||||
|
||||
aSizes->mDeadBlockList = gDeadBlockList->SizeOfIncludingThis(MallocSizeOf);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1644,6 +1741,8 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
|
||||
mode = "live";
|
||||
} else if (gOptions->IsDarkMatterMode()) {
|
||||
mode = "dark-matter";
|
||||
} else if (gOptions->IsCumulativeMode()) {
|
||||
mode = "cumulative";
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
mode = "(unknown DMD mode)";
|
||||
@ -1659,6 +1758,7 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
|
||||
|
||||
writer.StartArrayProperty("blockList");
|
||||
{
|
||||
// Live blocks.
|
||||
for (auto r = gLiveBlockTable->all(); !r.empty(); r.popFront()) {
|
||||
const LiveBlock& b = r.front();
|
||||
b.AddStackTracesToTable(usedStackTraces);
|
||||
@ -1688,6 +1788,24 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
|
||||
}
|
||||
writer.EndObject();
|
||||
}
|
||||
|
||||
// Dead blocks.
|
||||
for (auto iter = gDeadBlockList->Iter(); !iter.Done(); iter.Next()) {
|
||||
const DeadBlock& b = iter.Get();
|
||||
b.AddStackTracesToTable(usedStackTraces);
|
||||
|
||||
writer.StartObjectElement(writer.SingleLineStyle);
|
||||
{
|
||||
if (!b.IsSampled()) {
|
||||
writer.IntProperty("req", b.ReqSize());
|
||||
if (b.SlopSize() > 0) {
|
||||
writer.IntProperty("slop", b.SlopSize());
|
||||
}
|
||||
}
|
||||
writer.StringProperty("alloc", isc.ToIdString(b.AllocStackTrace()));
|
||||
}
|
||||
writer.EndObject();
|
||||
}
|
||||
}
|
||||
writer.EndArray();
|
||||
|
||||
@ -1761,6 +1879,10 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
|
||||
Show(gLiveBlockTable->capacity(), buf2, kBufLen),
|
||||
Show(gLiveBlockTable->count(), buf3, kBufLen));
|
||||
|
||||
StatusMsg(" Dead block list: %10s bytes (%s entries)\n",
|
||||
Show(sizes.mDeadBlockList, buf1, kBufLen),
|
||||
Show(gDeadBlockList->Length(), buf2, kBufLen));
|
||||
|
||||
StatusMsg(" }\n");
|
||||
StatusMsg(" Data structures that are destroyed after Dump() ends {\n");
|
||||
|
||||
@ -1819,6 +1941,7 @@ DMDFuncs::ResetEverything(const char* aOptions)
|
||||
|
||||
// Clear all existing blocks.
|
||||
gLiveBlockTable->clear();
|
||||
gDeadBlockList->Clear();
|
||||
gSmallBlockActualSizeCounter = 0;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ struct Sizes
|
||||
size_t mStackTracesUnused;
|
||||
size_t mStackTraceTable;
|
||||
size_t mLiveBlockTable;
|
||||
size_t mDeadBlockList;
|
||||
|
||||
Sizes() { Clear(); }
|
||||
void Clear() { memset(this, 0, sizeof(Sizes)); }
|
||||
@ -156,7 +157,7 @@ ClearReports()
|
||||
// "dmdEnvVar": "1",
|
||||
//
|
||||
// // The profiling mode. A mandatory string taking one of the following
|
||||
// // values: "live", "dark-matter".
|
||||
// // values: "live", "dark-matter", "cumulative".
|
||||
// "mode": "dark-matter",
|
||||
//
|
||||
// // The value of the --sample-below-size option. A mandatory integer.
|
||||
|
@ -280,7 +280,7 @@ def getDigestFromFile(args, inputFile):
|
||||
traceTable = j['traceTable']
|
||||
frameTable = j['frameTable']
|
||||
|
||||
if not mode in ['live', 'dark-matter']:
|
||||
if not mode in ['live', 'dark-matter', 'cumulative']:
|
||||
raise Exception("bad 'mode' property: '{:s}'".format(mode))
|
||||
|
||||
heapIsSampled = sampleBelowSize > 1 # is sampling present?
|
||||
@ -336,8 +336,8 @@ def getDigestFromFile(args, inputFile):
|
||||
# Aggregate blocks into records. All sufficiently similar blocks go into a
|
||||
# single record.
|
||||
|
||||
if mode == 'live':
|
||||
liveRecords = collections.defaultdict(Record)
|
||||
if mode in ['live', 'cumulative']:
|
||||
liveOrCumulativeRecords = collections.defaultdict(Record)
|
||||
elif mode == 'dark-matter':
|
||||
unreportedRecords = collections.defaultdict(Record)
|
||||
onceReportedRecords = collections.defaultdict(Record)
|
||||
@ -369,9 +369,9 @@ def getDigestFromFile(args, inputFile):
|
||||
traceTable[traceKey]))
|
||||
|
||||
allocatedAtTraceKey = block['alloc']
|
||||
if mode == 'live':
|
||||
if mode in ['live', 'cumulative']:
|
||||
recordKey = makeRecordKeyPart(allocatedAtTraceKey)
|
||||
records = liveRecords
|
||||
records = liveOrCumulativeRecords
|
||||
elif mode == 'dark-matter':
|
||||
recordKey = makeRecordKeyPart(allocatedAtTraceKey)
|
||||
if 'reps' in block:
|
||||
@ -414,7 +414,7 @@ def getDigestFromFile(args, inputFile):
|
||||
buildTraceDescription(traceTable, frameTable,
|
||||
allocatedAtTraceKey)
|
||||
|
||||
if mode == 'live':
|
||||
if mode in ['live', 'cumulative']:
|
||||
pass
|
||||
elif mode == 'dark-matter':
|
||||
if 'reps' in block and record.reportedAtDescs == []:
|
||||
@ -430,8 +430,8 @@ def getDigestFromFile(args, inputFile):
|
||||
digest['heapUsableSize'] = heapUsableSize
|
||||
digest['heapBlocks'] = heapBlocks
|
||||
digest['heapIsSampled'] = heapIsSampled
|
||||
if mode == 'live':
|
||||
digest['liveRecords'] = liveRecords
|
||||
if mode in ['live', 'cumulative']:
|
||||
digest['liveOrCumulativeRecords'] = liveOrCumulativeRecords
|
||||
elif mode == 'dark-matter':
|
||||
digest['unreportedRecords'] = unreportedRecords
|
||||
digest['onceReportedRecords'] = onceReportedRecords
|
||||
@ -475,9 +475,10 @@ def diffDigests(args, d1, d2):
|
||||
d3['heapUsableSize'] = d2['heapUsableSize'] - d1['heapUsableSize']
|
||||
d3['heapBlocks'] = d2['heapBlocks'] - d1['heapBlocks']
|
||||
d3['heapIsSampled'] = d2['heapIsSampled'] or d1['heapIsSampled']
|
||||
if d1['mode'] == 'live':
|
||||
d3['liveRecords'] = diffRecords(args, d1['liveRecords'],
|
||||
d2['liveRecords'])
|
||||
if d1['mode'] in ['live', 'cumulative']:
|
||||
d3['liveOrCumulativeRecords'] = \
|
||||
diffRecords(args, d1['liveOrCumulativeRecords'],
|
||||
d2['liveOrCumulativeRecords'])
|
||||
elif d1['mode'] == 'dark-matter':
|
||||
d3['unreportedRecords'] = diffRecords(args, d1['unreportedRecords'],
|
||||
d2['unreportedRecords'])
|
||||
@ -495,8 +496,8 @@ def printDigest(args, digest):
|
||||
heapUsableSize = digest['heapUsableSize']
|
||||
heapIsSampled = digest['heapIsSampled']
|
||||
heapBlocks = digest['heapBlocks']
|
||||
if mode == 'live':
|
||||
liveRecords = digest['liveRecords']
|
||||
if mode in ['live', 'cumulative']:
|
||||
liveOrCumulativeRecords = digest['liveOrCumulativeRecords']
|
||||
elif mode == 'dark-matter':
|
||||
unreportedRecords = digest['unreportedRecords']
|
||||
onceReportedRecords = digest['onceReportedRecords']
|
||||
@ -589,7 +590,7 @@ def printDigest(args, digest):
|
||||
out(' {:4.2f}% of the heap ({:4.2f}% cumulative)'.
|
||||
format(perc(record.usableSize, heapUsableSize),
|
||||
perc(kindCumulativeUsableSize, heapUsableSize)))
|
||||
if mode == 'live':
|
||||
if mode in ['live', 'cumulative']:
|
||||
pass
|
||||
elif mode == 'dark-matter':
|
||||
out(' {:4.2f}% of {:} ({:4.2f}% cumulative)'.
|
||||
@ -599,7 +600,7 @@ def printDigest(args, digest):
|
||||
out(' Allocated at {')
|
||||
printStack(record.allocatedAtDesc)
|
||||
out(' }')
|
||||
if mode == 'live':
|
||||
if mode in ['live', 'cumulative']:
|
||||
pass
|
||||
elif mode == 'dark-matter':
|
||||
for n, reportedAtDesc in enumerate(record.reportedAtDescs):
|
||||
@ -631,9 +632,9 @@ def printDigest(args, digest):
|
||||
printInvocation(' 2', dmdEnvVar[1], sampleBelowSize[1])
|
||||
|
||||
# Print records.
|
||||
if mode == 'live':
|
||||
liveUsableSize, liveBlocks = \
|
||||
printRecords('live', liveRecords, heapUsableSize)
|
||||
if mode in ['live', 'cumulative']:
|
||||
liveOrCumulativeUsableSize, liveOrCumulativeBlocks = \
|
||||
printRecords(mode, liveOrCumulativeRecords, heapUsableSize)
|
||||
elif mode == 'dark-matter':
|
||||
twiceReportedUsableSize, twiceReportedBlocks = \
|
||||
printRecords('twice-reported', twiceReportedRecords, heapUsableSize)
|
||||
@ -647,10 +648,10 @@ def printDigest(args, digest):
|
||||
# Print summary.
|
||||
out(separator)
|
||||
out('Summary {')
|
||||
if mode == 'live':
|
||||
if mode in ['live', 'cumulative']:
|
||||
out(' Total: {:} bytes in {:} blocks'.
|
||||
format(number(liveUsableSize, heapIsSampled),
|
||||
number(liveBlocks, heapIsSampled)))
|
||||
format(number(liveOrCumulativeUsableSize, heapIsSampled),
|
||||
number(liveOrCumulativeBlocks, heapIsSampled)))
|
||||
elif mode == 'dark-matter':
|
||||
fmt = ' {:15} {:>12} bytes ({:6.2f}%) in {:>7} blocks ({:6.2f}%)'
|
||||
out(fmt.
|
||||
|
@ -126,6 +126,9 @@ TestUnsampled(const char* aTestName, int aNum, const char* aMode, int aSeven)
|
||||
}
|
||||
free(a);
|
||||
|
||||
// A no-op.
|
||||
free(nullptr);
|
||||
|
||||
// Note: 8 bytes is the smallest requested size that gives consistent
|
||||
// behaviour across all platforms with jemalloc.
|
||||
// Analyze 1: reported.
|
||||
@ -141,7 +144,7 @@ TestUnsampled(const char* aTestName, int aNum, const char* aMode, int aSeven)
|
||||
// ReportOnAlloc, then freed.
|
||||
// Analyze 1: freed, irrelevant.
|
||||
// Analyze 2: freed, irrelevant.
|
||||
char* b2 = (char*) malloc(1);
|
||||
char* b2 = (char*) malloc(8);
|
||||
ReportOnAlloc(b2);
|
||||
free(b2);
|
||||
|
||||
@ -346,11 +349,13 @@ RunTests()
|
||||
|
||||
TestEmpty("empty", "live");
|
||||
TestEmpty("empty", "dark-matter");
|
||||
TestEmpty("empty", "cumulative");
|
||||
|
||||
TestUnsampled("unsampled", 1, "live", seven);
|
||||
TestUnsampled("unsampled", 1, "dark-matter", seven);
|
||||
|
||||
TestUnsampled("unsampled", 2, "dark-matter", seven);
|
||||
TestUnsampled("unsampled", 2, "cumulative", seven);
|
||||
|
||||
TestSampled("sampled", "live", seven);
|
||||
}
|
||||
|
18
memory/replace/dmd/test/full-empty-cumulative-expected.txt
Normal file
18
memory/replace/dmd/test/full-empty-cumulative-expected.txt
Normal file
@ -0,0 +1,18 @@
|
||||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-empty-cumulative-actual.txt full-empty-cumulative.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=cumulative --sample-below=1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
# no cumulative heap blocks
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: 0 bytes in 0 blocks
|
||||
}
|
||||
|
163
memory/replace/dmd/test/full-unsampled2-cumulative-expected.txt
Normal file
163
memory/replace/dmd/test/full-unsampled2-cumulative-expected.txt
Normal file
@ -0,0 +1,163 @@
|
||||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-unsampled2-cumulative-actual.txt full-unsampled2-cumulative.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=cumulative --sample-below=1 --show-dump-stats=yes'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 1 of 16
|
||||
8,192 bytes (4,097 requested / 4,095 slop)
|
||||
47.10% of the heap (47.10% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 2 of 16
|
||||
4,096 bytes (4,096 requested / 0 slop)
|
||||
23.55% of the heap (70.65% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
10 blocks in heap block record 3 of 16
|
||||
1,120 bytes (1,000 requested / 120 slop)
|
||||
Individual block sizes: 112 x 10
|
||||
6.44% of the heap (77.09% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 4 of 16
|
||||
1,024 bytes (1,024 requested / 0 slop)
|
||||
5.89% of the heap (82.98% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 5 of 16
|
||||
1,024 bytes (1,023 requested / 1 slop)
|
||||
5.89% of the heap (88.87% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
6 blocks in heap block record 6 of 16
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
3.04% of the heap (91.90% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
6 blocks in heap block record 7 of 16
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
3.04% of the heap (94.94% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 8 of 16
|
||||
512 bytes (512 requested / 0 slop)
|
||||
2.94% of the heap (97.88% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 9 of 16
|
||||
80 bytes (79 requested / 1 slop)
|
||||
0.46% of the heap (98.34% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 10 of 16
|
||||
80 bytes (78 requested / 2 slop)
|
||||
0.46% of the heap (98.80% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 11 of 16
|
||||
80 bytes (77 requested / 3 slop)
|
||||
0.46% of the heap (99.26% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 12 of 16
|
||||
64 bytes (64 requested / 0 slop)
|
||||
0.37% of the heap (99.63% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 13 of 16
|
||||
32 bytes (30 requested / 2 slop)
|
||||
0.18% of the heap (99.82% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 14 of 16
|
||||
16 bytes (10 requested / 6 slop)
|
||||
0.09% of the heap (99.91% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 15 of 16
|
||||
8 bytes (8 requested / 0 slop)
|
||||
0.05% of the heap (99.95% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
Cumulative {
|
||||
1 block in heap block record 16 of 16
|
||||
8 bytes (8 requested / 0 slop)
|
||||
0.05% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: 17,392 bytes in 35 blocks
|
||||
}
|
||||
|
@ -139,11 +139,13 @@ function run_test() {
|
||||
|
||||
test2("empty", "live");
|
||||
test2("empty", "dark-matter");
|
||||
test2("empty", "cumulative");
|
||||
|
||||
test2("unsampled1", "live");
|
||||
test2("unsampled1", "dark-matter");
|
||||
|
||||
test2("unsampled2", "dark-matter");
|
||||
test2("unsampled2", "cumulative");
|
||||
|
||||
test2("sampled", "live");
|
||||
|
||||
|
@ -2,9 +2,11 @@
|
||||
support-files =
|
||||
full-empty-live-expected.txt
|
||||
full-empty-dark-matter-expected.txt
|
||||
full-empty-cumulative-expected.txt
|
||||
full-unsampled1-live-expected.txt
|
||||
full-unsampled1-dark-matter-expected.txt
|
||||
full-unsampled2-dark-matter-expected.txt
|
||||
full-unsampled2-cumulative-expected.txt
|
||||
full-sampled-live-expected.txt
|
||||
script-max-frames.json
|
||||
script-max-frames-8-expected.txt
|
||||
|
@ -893,7 +893,7 @@ class RunProgram(MachCommandBase):
|
||||
@CommandArgumentGroup('DMD')
|
||||
@CommandArgument('--dmd', action='store_true', group='DMD',
|
||||
help='Enable DMD. The following arguments have no effect without this.')
|
||||
@CommandArgument('--mode', choices=['live', 'dark-matter'], group='DMD',
|
||||
@CommandArgument('--mode', choices=['live', 'dark-matter', 'cumulative'], group='DMD',
|
||||
help='Profiling mode. The default is \'dark-matter\'.')
|
||||
@CommandArgument('--sample-below', default=None, type=str, group='DMD',
|
||||
help='Sample blocks smaller than this. Use 1 for no sampling. The default is 4093.')
|
||||
|
@ -956,6 +956,10 @@ public:
|
||||
sizes.mLiveBlockTable,
|
||||
"Memory used by DMD's live block table.");
|
||||
|
||||
REPORT("explicit/dmd/dead-block-list",
|
||||
sizes.mDeadBlockList,
|
||||
"Memory used by DMD's dead block list.");
|
||||
|
||||
#undef REPORT
|
||||
|
||||
return NS_OK;
|
||||
|
Loading…
Reference in New Issue
Block a user