зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1035570 (part 3) - DMD: Add DMDAnalyzeHeap(), a heap snapshot function. r=erahm,mccr8.
The patch also adds DMDAnalyzeReports() as a synonym for DMDReportAndDump(), and deprecates the latter. --HG-- extra : rebase_source : 651246aa7a0a301f804c124f25beb0e8ed6cd67f
This commit is contained in:
Родитель
c375f811f6
Коммит
c07ebda3e2
|
@ -1440,8 +1440,27 @@ namespace dmd {
|
|||
// See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
|
||||
// how to use DMD.
|
||||
|
||||
static FILE *
|
||||
OpenDMDOutputFile(JSContext *cx, JS::CallArgs &args)
|
||||
{
|
||||
JSString *str = JS::ToString(cx, args.get(0));
|
||||
if (!str)
|
||||
return nullptr;
|
||||
JSAutoByteString pathname(cx, str);
|
||||
if (!pathname)
|
||||
return nullptr;
|
||||
|
||||
FILE* fp = fopen(pathname.ptr(), "w");
|
||||
if (!fp) {
|
||||
JS_ReportError(cx, "DMD can't open %s: %s",
|
||||
pathname.ptr(), strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
AnalyzeReports(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
if (!dmd::IsRunning()) {
|
||||
JS_ReportError(cx, "DMD is not running");
|
||||
|
@ -1449,24 +1468,48 @@ ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
|
|||
}
|
||||
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
JSString *str = JS::ToString(cx, args.get(0));
|
||||
if (!str)
|
||||
return false;
|
||||
JSAutoByteString pathname(cx, str);
|
||||
if (!pathname)
|
||||
return false;
|
||||
|
||||
FILE* fp = fopen(pathname.ptr(), "w");
|
||||
FILE *fp = OpenDMDOutputFile(cx, args);
|
||||
if (!fp) {
|
||||
JS_ReportError(cx, "DMD can't open %s: %s",
|
||||
pathname.ptr(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
dmd::ClearReports();
|
||||
dmd::RunReportersForThisProcess();
|
||||
dmd::Writer writer(FpWrite, fp);
|
||||
dmd::Dump(writer);
|
||||
dmd::AnalyzeReports(writer);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// This will be removed eventually.
|
||||
static bool
|
||||
ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
JS_ReportWarning(cx, "DMDReportAndDump() is deprecated; "
|
||||
"please use DMDAnalyzeReports() instead");
|
||||
|
||||
return AnalyzeReports(cx, argc, vp);
|
||||
}
|
||||
|
||||
static bool
|
||||
AnalyzeHeap(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
if (!dmd::IsRunning()) {
|
||||
JS_ReportError(cx, "DMD is not running");
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
FILE *fp = OpenDMDOutputFile(cx, args);
|
||||
if (!fp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dmd::Writer writer(FpWrite, fp);
|
||||
dmd::AnalyzeHeap(writer);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
|
@ -1478,7 +1521,9 @@ ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
|
|||
} // namespace mozilla
|
||||
|
||||
static const JSFunctionSpec DMDFunctions[] = {
|
||||
JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0),
|
||||
JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0),
|
||||
JS_FS("DMDAnalyzeReports", dmd::AnalyzeReports, 1, 0),
|
||||
JS_FS("DMDAnalyzeHeap", dmd::AnalyzeHeap, 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
// PAGE_SIZE. Nb: sysconf() is expensive, but it's only used for (the obsolete
|
||||
// and rarely used) valloc.
|
||||
#define MOZ_REPLACE_ONLY_MEMALIGN 1
|
||||
|
||||
#ifdef XP_WIN
|
||||
#define PAGE_SIZE GetPageSize()
|
||||
static long GetPageSize()
|
||||
|
@ -291,7 +292,7 @@ class Options
|
|||
Stress // do some performance stress tests
|
||||
};
|
||||
|
||||
char* mDMDEnvVar; // a saved copy, for printing during Dump()
|
||||
char* mDMDEnvVar; // a saved copy, for later printing
|
||||
|
||||
NumOption<size_t> mSampleBelowSize;
|
||||
NumOption<uint32_t> mMaxFrames;
|
||||
|
@ -610,7 +611,8 @@ class LocationService
|
|||
uint32_t mInUse:1; // is the entry used?
|
||||
|
||||
Entry()
|
||||
: mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0), mFileName(nullptr), mLineNo(0), mInUse(0)
|
||||
: mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0),
|
||||
mFileName(nullptr), mLineNo(0), mInUse(0)
|
||||
{}
|
||||
|
||||
~Entry()
|
||||
|
@ -648,12 +650,12 @@ class LocationService
|
|||
}
|
||||
};
|
||||
|
||||
// A direct-mapped cache. When doing a dump just after starting desktop
|
||||
// Firefox (which is similar to dumping after a longer-running session,
|
||||
// thanks to the limit on how many records we dump), a cache with 2^24
|
||||
// entries (which approximates an infinite-entry cache) has a ~91% hit rate.
|
||||
// A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB (on
|
||||
// 32-bit platforms) or ~150 KiB (on 64-bit platforms).
|
||||
// A direct-mapped cache. When doing AnalyzeReports just after starting
|
||||
// desktop Firefox (which is similar to analyzing after a longer-running
|
||||
// session, thanks to the limit on how many records we print), a cache with
|
||||
// 2^24 entries (which approximates an infinite-entry cache) has a ~91% hit
|
||||
// rate. A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB
|
||||
// (on 32-bit platforms) or ~150 KiB (on 64-bit platforms).
|
||||
static const size_t kNumEntries = 1 << 12;
|
||||
static const size_t kMask = kNumEntries - 1;
|
||||
Entry mEntries[kNumEntries];
|
||||
|
@ -793,7 +795,7 @@ public:
|
|||
|
||||
void Sort()
|
||||
{
|
||||
qsort(mPcs, mLength, sizeof(mPcs[0]), StackTrace::QsortCmp);
|
||||
qsort(mPcs, mLength, sizeof(mPcs[0]), StackTrace::Cmp);
|
||||
}
|
||||
|
||||
void Print(const Writer& aWriter, LocationService* aLocService) const;
|
||||
|
@ -823,7 +825,7 @@ private:
|
|||
st->mLength++;
|
||||
}
|
||||
|
||||
static int QsortCmp(const void* aA, const void* aB)
|
||||
static int Cmp(const void* aA, const void* aB)
|
||||
{
|
||||
const void* const a = *static_cast<const void* const*>(aA);
|
||||
const void* const b = *static_cast<const void* const*>(aB);
|
||||
|
@ -844,7 +846,7 @@ void
|
|||
StackTrace::Print(const Writer& aWriter, LocationService* aLocService) const
|
||||
{
|
||||
if (mLength == 0) {
|
||||
W(" (empty)\n"); // StackTrace::Get() must have failed
|
||||
W(" (empty)\n"); // StackTrace::Get() must have failed
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -973,8 +975,8 @@ class Block
|
|||
// - Ptr: |mReportStackTrace| - stack trace where this block was reported.
|
||||
// nullptr if not reported.
|
||||
// - Tag bit 0: |mReportedOnAlloc| - was the block reported immediately on
|
||||
// allocation? If so, DMD must not clear the report at the end of Dump().
|
||||
// Only relevant if |mReportStackTrace| is non-nullptr.
|
||||
// allocation? If so, DMD must not clear the report at the end of
|
||||
// AnalyzeReports(). Only relevant if |mReportStackTrace| is non-nullptr.
|
||||
//
|
||||
// |mPtr| is used as the key in BlockTable, so it's ok for this member
|
||||
// to be |mutable|.
|
||||
|
@ -1417,7 +1419,7 @@ public:
|
|||
mSampled = mSampled || aRecordSize.IsSampled();
|
||||
}
|
||||
|
||||
static int Cmp(const RecordSize& aA, const RecordSize& aB)
|
||||
static int CmpByUsable(const RecordSize& aA, const RecordSize& aB)
|
||||
{
|
||||
// Primary sort: put bigger usable sizes first.
|
||||
if (aA.Usable() > aB.Usable()) return -1;
|
||||
|
@ -1464,14 +1466,15 @@ public:
|
|||
void Print(const Writer& aWriter, LocationService* aLocService,
|
||||
uint32_t aM, uint32_t aN, const char* aStr, const char* astr,
|
||||
size_t aCategoryUsableSize, size_t aCumulativeUsableSize,
|
||||
size_t aTotalUsableSize) const;
|
||||
size_t aTotalUsableSize, bool aShowCategoryPercentage,
|
||||
bool aShowReportedAt) const;
|
||||
|
||||
static int QsortCmp(const void* aA, const void* aB)
|
||||
static int CmpByUsable(const void* aA, const void* aB)
|
||||
{
|
||||
const Record* const a = *static_cast<const Record* const*>(aA);
|
||||
const Record* const b = *static_cast<const Record* const*>(aB);
|
||||
|
||||
return RecordSize::Cmp(a->mRecordSize, b->mRecordSize);
|
||||
return RecordSize::CmpByUsable(a->mRecordSize, b->mRecordSize);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1481,7 +1484,8 @@ void
|
|||
Record::Print(const Writer& aWriter, LocationService* aLocService,
|
||||
uint32_t aM, uint32_t aN, const char* aStr, const char* astr,
|
||||
size_t aCategoryUsableSize, size_t aCumulativeUsableSize,
|
||||
size_t aTotalUsableSize) const
|
||||
size_t aTotalUsableSize, bool aShowCategoryPercentage,
|
||||
bool aShowReportedAt) const
|
||||
{
|
||||
bool showTilde = mRecordSize.IsSampled();
|
||||
|
||||
|
@ -1500,24 +1504,28 @@ Record::Print(const Writer& aWriter, LocationService* aLocService,
|
|||
Percent(mRecordSize.Usable(), aTotalUsableSize),
|
||||
Percent(aCumulativeUsableSize, aTotalUsableSize));
|
||||
|
||||
W(" %4.2f%% of %s (%4.2f%% cumulative)\n",
|
||||
Percent(mRecordSize.Usable(), aCategoryUsableSize),
|
||||
astr,
|
||||
Percent(aCumulativeUsableSize, aCategoryUsableSize));
|
||||
if (aShowCategoryPercentage) {
|
||||
W(" %4.2f%% of %s (%4.2f%% cumulative)\n",
|
||||
Percent(mRecordSize.Usable(), aCategoryUsableSize),
|
||||
astr,
|
||||
Percent(aCumulativeUsableSize, aCategoryUsableSize));
|
||||
}
|
||||
|
||||
W(" Allocated at {\n");
|
||||
mAllocStackTrace->Print(aWriter, aLocService);
|
||||
W(" }\n");
|
||||
|
||||
if (mReportStackTrace1) {
|
||||
W(" Reported at {\n");
|
||||
mReportStackTrace1->Print(aWriter, aLocService);
|
||||
W(" }\n");
|
||||
}
|
||||
if (mReportStackTrace2) {
|
||||
W(" Reported again at {\n");
|
||||
mReportStackTrace2->Print(aWriter, aLocService);
|
||||
W(" }\n");
|
||||
if (aShowReportedAt) {
|
||||
if (mReportStackTrace1) {
|
||||
W(" Reported at {\n");
|
||||
mReportStackTrace1->Print(aWriter, aLocService);
|
||||
W(" }\n");
|
||||
}
|
||||
if (mReportStackTrace2) {
|
||||
W(" Reported again at {\n");
|
||||
mReportStackTrace2->Print(aWriter, aLocService);
|
||||
W(" }\n");
|
||||
}
|
||||
}
|
||||
|
||||
W("}\n\n");
|
||||
|
@ -1808,9 +1816,11 @@ ReportOnAlloc(const void* aPtr)
|
|||
|
||||
static void
|
||||
PrintSortedRecords(const Writer& aWriter, LocationService* aLocService,
|
||||
int (*aCmp)(const void*, const void*),
|
||||
const char* aStr, const char* astr,
|
||||
const RecordTable& aRecordTable,
|
||||
size_t aCategoryUsableSize, size_t aTotalUsableSize)
|
||||
size_t aCategoryUsableSize, size_t aTotalUsableSize,
|
||||
bool aShowCategoryPercentage, bool aShowReportedAt)
|
||||
{
|
||||
StatusMsg(" creating and sorting %s heap block record array...\n", astr);
|
||||
|
||||
|
@ -1823,7 +1833,7 @@ PrintSortedRecords(const Writer& aWriter, LocationService* aLocService,
|
|||
recordArray.infallibleAppend(&r.front());
|
||||
}
|
||||
qsort(recordArray.begin(), recordArray.length(), sizeof(recordArray[0]),
|
||||
Record::QsortCmp);
|
||||
aCmp);
|
||||
|
||||
WriteSeparator();
|
||||
|
||||
|
@ -1845,7 +1855,8 @@ PrintSortedRecords(const Writer& aWriter, LocationService* aLocService,
|
|||
cumulativeUsableSize += r->GetRecordSize().Usable();
|
||||
if (i < maxRecords) {
|
||||
r->Print(aWriter, aLocService, i+1, numRecords, aStr, astr,
|
||||
aCategoryUsableSize, cumulativeUsableSize, aTotalUsableSize);
|
||||
aCategoryUsableSize, cumulativeUsableSize, aTotalUsableSize,
|
||||
aShowCategoryPercentage, aShowReportedAt);
|
||||
} else if (i == maxRecords) {
|
||||
W("# %s: stopping after %s heap block records\n\n", aStr,
|
||||
Show(maxRecords, gBuf1, kBufLen));
|
||||
|
@ -1910,10 +1921,14 @@ SizeOf(Sizes* aSizes)
|
|||
SizeOfInternal(aSizes);
|
||||
}
|
||||
|
||||
void
|
||||
ClearReportsInternal()
|
||||
MOZ_EXPORT void
|
||||
ClearReports()
|
||||
{
|
||||
MOZ_ASSERT(gStateLock->IsLocked());
|
||||
if (!gIsDMDRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoLockState lock;
|
||||
|
||||
// Unreport all blocks that were marked reported by a memory reporter. This
|
||||
// excludes those that were reported on allocation, because they need to keep
|
||||
|
@ -1923,77 +1938,230 @@ ClearReportsInternal()
|
|||
}
|
||||
}
|
||||
|
||||
MOZ_EXPORT void
|
||||
ClearReports()
|
||||
{
|
||||
if (!gIsDMDRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoLockState lock;
|
||||
ClearReportsInternal();
|
||||
}
|
||||
|
||||
MOZ_EXPORT bool
|
||||
IsRunning()
|
||||
{
|
||||
return gIsDMDRunning;
|
||||
}
|
||||
|
||||
MOZ_EXPORT void
|
||||
Dump(Writer aWriter)
|
||||
// AnalyzeReports() and AnalyzeHeap() have a lot in common. This abstract class
|
||||
// encapsulates the operations that are not shared.
|
||||
class Analyzer
|
||||
{
|
||||
public:
|
||||
virtual const char* AnalyzeFunctionName() const = 0;
|
||||
|
||||
virtual RecordTable* ProcessBlock(const Block& aBlock) = 0;
|
||||
|
||||
virtual void PrintRecords(const Writer& aWriter,
|
||||
LocationService* aLocService) const = 0;
|
||||
virtual void PrintSummary(const Writer& aWriter, bool aShowTilde) const = 0;
|
||||
virtual void PrintStats(const Writer& aWriter) const = 0;
|
||||
|
||||
struct RecordKindData
|
||||
{
|
||||
RecordTable mRecordTable;
|
||||
size_t mUsableSize;
|
||||
size_t mNumBlocks;
|
||||
|
||||
RecordKindData(size_t aN)
|
||||
: mUsableSize(0), mNumBlocks(0)
|
||||
{
|
||||
mRecordTable.init(aN);
|
||||
}
|
||||
|
||||
void processBlock(const Block& aBlock)
|
||||
{
|
||||
mUsableSize += aBlock.UsableSize();
|
||||
mNumBlocks++;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class ReportsAnalyzer MOZ_FINAL : public Analyzer
|
||||
{
|
||||
RecordKindData mUnreported;
|
||||
RecordKindData mOnceReported;
|
||||
RecordKindData mTwiceReported;
|
||||
|
||||
size_t mTotalUsableSize;
|
||||
size_t mTotalNumBlocks;
|
||||
|
||||
public:
|
||||
ReportsAnalyzer()
|
||||
: mUnreported(1024), mOnceReported(1024), mTwiceReported(0),
|
||||
mTotalUsableSize(0), mTotalNumBlocks(0)
|
||||
{}
|
||||
|
||||
~ReportsAnalyzer()
|
||||
{
|
||||
ClearReports();
|
||||
}
|
||||
|
||||
virtual const char* AnalyzeFunctionName() const { return "AnalyzeReports"; }
|
||||
|
||||
virtual RecordTable* ProcessBlock(const Block& aBlock)
|
||||
{
|
||||
RecordKindData* data;
|
||||
uint32_t numReports = aBlock.NumReports();
|
||||
if (numReports == 0) {
|
||||
data = &mUnreported;
|
||||
} else if (numReports == 1) {
|
||||
data = &mOnceReported;
|
||||
} else {
|
||||
MOZ_ASSERT(numReports == 2);
|
||||
data = &mTwiceReported;
|
||||
}
|
||||
data->processBlock(aBlock);
|
||||
|
||||
mTotalUsableSize += aBlock.UsableSize();
|
||||
mTotalNumBlocks++;
|
||||
|
||||
return &data->mRecordTable;
|
||||
}
|
||||
|
||||
virtual void PrintRecords(const Writer& aWriter,
|
||||
LocationService* aLocService) const
|
||||
{
|
||||
PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
|
||||
"Twice-reported", "twice-reported",
|
||||
mTwiceReported.mRecordTable,
|
||||
mTwiceReported.mUsableSize, mTotalUsableSize,
|
||||
/* showCategoryPercentage = */ true,
|
||||
/* showReportedAt = */ true);
|
||||
|
||||
PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
|
||||
"Unreported", "unreported",
|
||||
mUnreported.mRecordTable,
|
||||
mUnreported.mUsableSize, mTotalUsableSize,
|
||||
/* showCategoryPercentage = */ true,
|
||||
/* showReportedAt = */ true);
|
||||
|
||||
PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
|
||||
"Once-reported", "once-reported",
|
||||
mOnceReported.mRecordTable,
|
||||
mOnceReported.mUsableSize, mTotalUsableSize,
|
||||
/* showCategoryPercentage = */ true,
|
||||
/* showReportedAt = */ true);
|
||||
}
|
||||
|
||||
virtual void PrintSummary(const Writer& aWriter, bool aShowTilde) const
|
||||
{
|
||||
W(" Total: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(mTotalUsableSize, gBuf1, kBufLen, aShowTilde),
|
||||
100.0,
|
||||
Show(mTotalNumBlocks, gBuf2, kBufLen, aShowTilde),
|
||||
100.0);
|
||||
|
||||
W(" Unreported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(mUnreported.mUsableSize, gBuf1, kBufLen, aShowTilde),
|
||||
Percent(mUnreported.mUsableSize, mTotalUsableSize),
|
||||
Show(mUnreported.mNumBlocks, gBuf2, kBufLen, aShowTilde),
|
||||
Percent(mUnreported.mNumBlocks, mTotalNumBlocks));
|
||||
|
||||
W(" Once-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(mOnceReported.mUsableSize, gBuf1, kBufLen, aShowTilde),
|
||||
Percent(mOnceReported.mUsableSize, mTotalUsableSize),
|
||||
Show(mOnceReported.mNumBlocks, gBuf2, kBufLen, aShowTilde),
|
||||
Percent(mOnceReported.mNumBlocks, mTotalNumBlocks));
|
||||
|
||||
W(" Twice-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(mTwiceReported.mUsableSize, gBuf1, kBufLen, aShowTilde),
|
||||
Percent(mTwiceReported.mUsableSize, mTotalUsableSize),
|
||||
Show(mTwiceReported.mNumBlocks, gBuf2, kBufLen, aShowTilde),
|
||||
Percent(mTwiceReported.mNumBlocks, mTotalNumBlocks));
|
||||
}
|
||||
|
||||
virtual void PrintStats(const Writer& aWriter) const
|
||||
{
|
||||
size_t unreportedSize =
|
||||
mUnreported.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Unreported table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(unreportedSize, gBuf1, kBufLen),
|
||||
Show(mUnreported.mRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(mUnreported.mRecordTable.count(), gBuf3, kBufLen));
|
||||
|
||||
size_t onceReportedSize =
|
||||
mOnceReported.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Once-reported table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(onceReportedSize, gBuf1, kBufLen),
|
||||
Show(mOnceReported.mRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(mOnceReported.mRecordTable.count(), gBuf3, kBufLen));
|
||||
|
||||
size_t twiceReportedSize =
|
||||
mTwiceReported.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Twice-reported table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(twiceReportedSize, gBuf1, kBufLen),
|
||||
Show(mTwiceReported.mRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(mTwiceReported.mRecordTable.count(), gBuf3, kBufLen));
|
||||
}
|
||||
};
|
||||
|
||||
class HeapAnalyzer MOZ_FINAL : public Analyzer
|
||||
{
|
||||
RecordKindData mLive;
|
||||
|
||||
public:
|
||||
HeapAnalyzer() : mLive(1024) {}
|
||||
|
||||
virtual const char* AnalyzeFunctionName() const { return "AnalyzeHeap"; }
|
||||
|
||||
virtual RecordTable* ProcessBlock(const Block& aBlock)
|
||||
{
|
||||
mLive.processBlock(aBlock);
|
||||
|
||||
return &mLive.mRecordTable;
|
||||
}
|
||||
|
||||
virtual void PrintRecords(const Writer& aWriter,
|
||||
LocationService* aLocService) const
|
||||
{
|
||||
size_t totalUsableSize = mLive.mUsableSize;
|
||||
PrintSortedRecords(aWriter, aLocService, Record::CmpByUsable,
|
||||
"Live", "live", mLive.mRecordTable, totalUsableSize,
|
||||
mLive.mUsableSize,
|
||||
/* showReportedAt = */ false,
|
||||
/* showCategoryPercentage = */ false);
|
||||
}
|
||||
|
||||
virtual void PrintSummary(const Writer& aWriter, bool aShowTilde) const
|
||||
{
|
||||
W(" Total: %s bytes in %s blocks\n",
|
||||
Show(mLive.mUsableSize, gBuf1, kBufLen, aShowTilde),
|
||||
Show(mLive.mNumBlocks, gBuf2, kBufLen, aShowTilde));
|
||||
}
|
||||
|
||||
virtual void PrintStats(const Writer& aWriter) const
|
||||
{
|
||||
size_t liveSize = mLive.mRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Live table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(liveSize, gBuf1, kBufLen),
|
||||
Show(mLive.mRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(mLive.mRecordTable.count(), gBuf3, kBufLen));
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
AnalyzeImpl(Analyzer *aAnalyzer, const Writer& aWriter)
|
||||
{
|
||||
if (!gIsDMDRunning) {
|
||||
const char* msg = "cannot Dump(); DMD was not enabled at startup\n";
|
||||
StatusMsg("%s", msg);
|
||||
W("%s", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
AutoBlockIntercepts block(Thread::Fetch());
|
||||
AutoLockState lock;
|
||||
|
||||
static int dumpCount = 1;
|
||||
StatusMsg("Dump %d {\n", dumpCount++);
|
||||
static int analysisCount = 1;
|
||||
StatusMsg("%s %d {\n", aAnalyzer->AnalyzeFunctionName(), analysisCount++);
|
||||
|
||||
StatusMsg(" gathering heap block records...\n");
|
||||
|
||||
RecordTable unreportedRecordTable;
|
||||
(void)unreportedRecordTable.init(1024);
|
||||
size_t unreportedUsableSize = 0;
|
||||
size_t unreportedNumBlocks = 0;
|
||||
|
||||
RecordTable onceReportedRecordTable;
|
||||
(void)onceReportedRecordTable.init(1024);
|
||||
size_t onceReportedUsableSize = 0;
|
||||
size_t onceReportedNumBlocks = 0;
|
||||
|
||||
RecordTable twiceReportedRecordTable;
|
||||
(void)twiceReportedRecordTable.init(0);
|
||||
size_t twiceReportedUsableSize = 0;
|
||||
size_t twiceReportedNumBlocks = 0;
|
||||
|
||||
bool anyBlocksSampled = false;
|
||||
|
||||
for (BlockTable::Range r = gBlockTable->all(); !r.empty(); r.popFront()) {
|
||||
const Block& b = r.front();
|
||||
RecordTable* table = aAnalyzer->ProcessBlock(b);
|
||||
|
||||
RecordTable* table;
|
||||
uint32_t numReports = b.NumReports();
|
||||
if (numReports == 0) {
|
||||
unreportedUsableSize += b.UsableSize();
|
||||
unreportedNumBlocks++;
|
||||
table = &unreportedRecordTable;
|
||||
} else if (numReports == 1) {
|
||||
onceReportedUsableSize += b.UsableSize();
|
||||
onceReportedNumBlocks++;
|
||||
table = &onceReportedRecordTable;
|
||||
} else {
|
||||
MOZ_ASSERT(numReports == 2);
|
||||
twiceReportedUsableSize += b.UsableSize();
|
||||
twiceReportedNumBlocks++;
|
||||
table = &twiceReportedRecordTable;
|
||||
}
|
||||
RecordKey key(b);
|
||||
RecordTable::AddPtr p = table->lookupForAdd(key);
|
||||
if (!p) {
|
||||
|
@ -2004,63 +2172,24 @@ Dump(Writer aWriter)
|
|||
|
||||
anyBlocksSampled = anyBlocksSampled || b.IsSampled();
|
||||
}
|
||||
size_t totalUsableSize =
|
||||
unreportedUsableSize + onceReportedUsableSize + twiceReportedUsableSize;
|
||||
size_t totalNumBlocks =
|
||||
unreportedNumBlocks + onceReportedNumBlocks + twiceReportedNumBlocks;
|
||||
|
||||
WriteSeparator();
|
||||
W("Invocation {\n");
|
||||
W(" $DMD = '%s'\n", gOptions->DMDEnvVar());
|
||||
W(" Function = %s\n", aAnalyzer->AnalyzeFunctionName());
|
||||
W(" Sample-below size = %lld\n", (long long)(gOptions->SampleBelowSize()));
|
||||
W("}\n\n");
|
||||
|
||||
// Allocate this on the heap instead of the stack because it's fairly large.
|
||||
LocationService* locService = InfallibleAllocPolicy::new_<LocationService>();
|
||||
|
||||
PrintSortedRecords(aWriter, locService,
|
||||
"Twice-reported", "twice-reported",
|
||||
twiceReportedRecordTable,
|
||||
twiceReportedUsableSize, totalUsableSize);
|
||||
|
||||
PrintSortedRecords(aWriter, locService,
|
||||
"Unreported", "unreported",
|
||||
unreportedRecordTable,
|
||||
unreportedUsableSize, totalUsableSize);
|
||||
|
||||
PrintSortedRecords(aWriter, locService,
|
||||
"Once-reported", "once-reported",
|
||||
onceReportedRecordTable,
|
||||
onceReportedUsableSize, totalUsableSize);
|
||||
|
||||
bool showTilde = anyBlocksSampled;
|
||||
aAnalyzer->PrintRecords(aWriter, locService);
|
||||
|
||||
WriteSeparator();
|
||||
W("Summary {\n");
|
||||
|
||||
W(" Total: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(totalUsableSize, gBuf1, kBufLen, showTilde),
|
||||
100.0,
|
||||
Show(totalNumBlocks, gBuf2, kBufLen, showTilde),
|
||||
100.0);
|
||||
|
||||
W(" Unreported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(unreportedUsableSize, gBuf1, kBufLen, showTilde),
|
||||
Percent(unreportedUsableSize, totalUsableSize),
|
||||
Show(unreportedNumBlocks, gBuf2, kBufLen, showTilde),
|
||||
Percent(unreportedNumBlocks, totalNumBlocks));
|
||||
|
||||
W(" Once-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(onceReportedUsableSize, gBuf1, kBufLen, showTilde),
|
||||
Percent(onceReportedUsableSize, totalUsableSize),
|
||||
Show(onceReportedNumBlocks, gBuf2, kBufLen, showTilde),
|
||||
Percent(onceReportedNumBlocks, totalNumBlocks));
|
||||
|
||||
W(" Twice-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
|
||||
Show(twiceReportedUsableSize, gBuf1, kBufLen, showTilde),
|
||||
Percent(twiceReportedUsableSize, totalUsableSize),
|
||||
Show(twiceReportedNumBlocks, gBuf2, kBufLen, showTilde),
|
||||
Percent(twiceReportedNumBlocks, totalNumBlocks));
|
||||
bool showTilde = anyBlocksSampled;
|
||||
aAnalyzer->PrintSummary(aWriter, showTilde);
|
||||
|
||||
W("}\n\n");
|
||||
|
||||
|
@ -2072,7 +2201,7 @@ Dump(Writer aWriter)
|
|||
WriteSeparator();
|
||||
W("Execution measurements {\n");
|
||||
|
||||
W(" Data structures that persist after Dump() ends:\n");
|
||||
W(" Data structures that persist after Dump() ends {\n");
|
||||
|
||||
W(" Used stack traces: %10s bytes\n",
|
||||
Show(sizes.mStackTracesUsed, gBuf1, kBufLen));
|
||||
|
@ -2090,33 +2219,16 @@ Dump(Writer aWriter)
|
|||
Show(gBlockTable->capacity(), gBuf2, kBufLen),
|
||||
Show(gBlockTable->count(), gBuf3, kBufLen));
|
||||
|
||||
W("\n Data structures that are destroyed after Dump() ends:\n");
|
||||
W(" }\n");
|
||||
W(" Data structures that are destroyed after Dump() ends {\n");
|
||||
|
||||
size_t unreportedSize =
|
||||
unreportedRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Unreported table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(unreportedSize, gBuf1, kBufLen),
|
||||
Show(unreportedRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(unreportedRecordTable.count(), gBuf3, kBufLen));
|
||||
|
||||
size_t onceReportedSize =
|
||||
onceReportedRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Once-reported table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(onceReportedSize, gBuf1, kBufLen),
|
||||
Show(onceReportedRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(onceReportedRecordTable.count(), gBuf3, kBufLen));
|
||||
|
||||
size_t twiceReportedSize =
|
||||
twiceReportedRecordTable.sizeOfIncludingThis(MallocSizeOf);
|
||||
W(" Twice-reported table: %10s bytes (%s entries, %s used)\n",
|
||||
Show(twiceReportedSize, gBuf1, kBufLen),
|
||||
Show(twiceReportedRecordTable.capacity(), gBuf2, kBufLen),
|
||||
Show(twiceReportedRecordTable.count(), gBuf3, kBufLen));
|
||||
aAnalyzer->PrintStats(aWriter);
|
||||
|
||||
W(" Location service: %10s bytes\n",
|
||||
Show(locService->SizeOfIncludingThis(), gBuf1, kBufLen));
|
||||
|
||||
W("\n Counts:\n");
|
||||
W(" }\n");
|
||||
W(" Counts {\n");
|
||||
|
||||
size_t hits = locService->NumCacheHits();
|
||||
size_t misses = locService->NumCacheMisses();
|
||||
|
@ -2130,16 +2242,29 @@ Dump(Writer aWriter)
|
|||
"%4.1f%% hit rate, %.1f%% occupancy at end\n",
|
||||
Percent(hits, requests), Percent(count, capacity));
|
||||
|
||||
W(" }\n");
|
||||
W("}\n\n");
|
||||
}
|
||||
|
||||
InfallibleAllocPolicy::delete_(locService);
|
||||
|
||||
ClearReportsInternal(); // Use internal version, we already have the lock.
|
||||
|
||||
StatusMsg("}\n");
|
||||
}
|
||||
|
||||
MOZ_EXPORT void
|
||||
AnalyzeReports(const Writer& aWriter)
|
||||
{
|
||||
ReportsAnalyzer aAnalyzer;
|
||||
AnalyzeImpl(&aAnalyzer, aWriter);
|
||||
}
|
||||
|
||||
MOZ_EXPORT void
|
||||
AnalyzeHeap(const Writer& aWriter)
|
||||
{
|
||||
HeapAnalyzer analyzer;
|
||||
AnalyzeImpl(&analyzer, aWriter);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Testing
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -2180,11 +2305,12 @@ RunTestMode(FILE* fp)
|
|||
// The first part of this test requires sampling to be disabled.
|
||||
gOptions->SetSampleBelowSize(1);
|
||||
|
||||
// Dump 1. Zero for everything.
|
||||
Dump(writer);
|
||||
// AnalyzeReports 1. Zero for everything.
|
||||
AnalyzeReports(writer);
|
||||
AnalyzeHeap(writer);
|
||||
|
||||
// Dump 2: 1 freed, 9 out of 10 unreported.
|
||||
// Dump 3: still present and unreported.
|
||||
// AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
|
||||
// AnalyzeReports 3: still present and unreported.
|
||||
int i;
|
||||
char* a;
|
||||
for (i = 0; i < 10; i++) {
|
||||
|
@ -2194,94 +2320,94 @@ RunTestMode(FILE* fp)
|
|||
free(a);
|
||||
|
||||
// Min-sized block.
|
||||
// Dump 2: reported.
|
||||
// Dump 3: thrice-reported.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: thrice-reported.
|
||||
char* a2 = (char*) malloc(0);
|
||||
Report(a2);
|
||||
|
||||
// Operator new[].
|
||||
// Dump 2: reported.
|
||||
// Dump 3: reportedness carries over, due to ReportOnAlloc.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
|
||||
char* b = new char[10];
|
||||
ReportOnAlloc(b);
|
||||
|
||||
// ReportOnAlloc, then freed.
|
||||
// Dump 2: freed, irrelevant.
|
||||
// Dump 3: freed, irrelevant.
|
||||
// AnalyzeReports 2: freed, irrelevant.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* b2 = new char;
|
||||
ReportOnAlloc(b2);
|
||||
free(b2);
|
||||
|
||||
// Dump 2: reported 4 times.
|
||||
// Dump 3: freed, irrelevant.
|
||||
// AnalyzeReports 2: reported 4 times.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* c = (char*) calloc(10, 3);
|
||||
Report(c);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Report(c);
|
||||
}
|
||||
|
||||
// Dump 2: ignored.
|
||||
// Dump 3: irrelevant.
|
||||
// AnalyzeReports 2: ignored.
|
||||
// AnalyzeReports 3: irrelevant.
|
||||
Report((void*)(intptr_t)i);
|
||||
|
||||
// jemalloc rounds this up to 8192.
|
||||
// Dump 2: reported.
|
||||
// Dump 3: freed.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: freed.
|
||||
char* e = (char*) malloc(4096);
|
||||
e = (char*) realloc(e, 4097);
|
||||
Report(e);
|
||||
|
||||
// First realloc is like malloc; second realloc is shrinking.
|
||||
// Dump 2: reported.
|
||||
// Dump 3: re-reported.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: re-reported.
|
||||
char* e2 = (char*) realloc(nullptr, 1024);
|
||||
e2 = (char*) realloc(e2, 512);
|
||||
Report(e2);
|
||||
|
||||
// First realloc is like malloc; second realloc creates a min-sized block.
|
||||
// XXX: on Windows, second realloc frees the block.
|
||||
// Dump 2: reported.
|
||||
// Dump 3: freed, irrelevant.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* e3 = (char*) realloc(nullptr, 1023);
|
||||
//e3 = (char*) realloc(e3, 0);
|
||||
MOZ_ASSERT(e3);
|
||||
Report(e3);
|
||||
|
||||
// Dump 2: freed, irrelevant.
|
||||
// Dump 3: freed, irrelevant.
|
||||
// AnalyzeReports 2: freed, irrelevant.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* f = (char*) malloc(64);
|
||||
free(f);
|
||||
|
||||
// Dump 2: ignored.
|
||||
// Dump 3: irrelevant.
|
||||
// AnalyzeReports 2: ignored.
|
||||
// AnalyzeReports 3: irrelevant.
|
||||
Report((void*)(intptr_t)0x0);
|
||||
|
||||
// Dump 2: mixture of reported and unreported.
|
||||
// Dump 3: all unreported.
|
||||
// AnalyzeReports 2: mixture of reported and unreported.
|
||||
// AnalyzeReports 3: all unreported.
|
||||
foo();
|
||||
foo();
|
||||
|
||||
// Dump 2: twice-reported.
|
||||
// Dump 3: twice-reported.
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: twice-reported.
|
||||
char* g1 = (char*) malloc(77);
|
||||
ReportOnAlloc(g1);
|
||||
ReportOnAlloc(g1);
|
||||
|
||||
// Dump 2: twice-reported.
|
||||
// Dump 3: once-reported.
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: once-reported.
|
||||
char* g2 = (char*) malloc(78);
|
||||
Report(g2);
|
||||
ReportOnAlloc(g2);
|
||||
|
||||
// Dump 2: twice-reported.
|
||||
// Dump 3: once-reported.
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: once-reported.
|
||||
char* g3 = (char*) malloc(79);
|
||||
ReportOnAlloc(g3);
|
||||
Report(g3);
|
||||
|
||||
// All the odd-ball ones.
|
||||
// Dump 2: all unreported.
|
||||
// Dump 3: all freed, irrelevant.
|
||||
// AnalyzeReports 2: all unreported.
|
||||
// AnalyzeReports 3: all freed, irrelevant.
|
||||
// XXX: no memalign on Mac
|
||||
//void* x = memalign(64, 65); // rounds up to 128
|
||||
//UseItOrLoseIt(x);
|
||||
|
@ -2294,8 +2420,9 @@ RunTestMode(FILE* fp)
|
|||
//UseItOrLoseIt(z);
|
||||
//aligned_alloc(64, 256); // XXX: C11 only
|
||||
|
||||
// Dump 2.
|
||||
Dump(writer);
|
||||
// AnalyzeReports 2.
|
||||
AnalyzeReports(writer);
|
||||
AnalyzeHeap(writer);
|
||||
|
||||
//---------
|
||||
|
||||
|
@ -2309,8 +2436,9 @@ RunTestMode(FILE* fp)
|
|||
//free(y);
|
||||
//free(z);
|
||||
|
||||
// Dump 3.
|
||||
Dump(writer);
|
||||
// AnalyzeReports 3.
|
||||
AnalyzeReports(writer);
|
||||
AnalyzeHeap(writer);
|
||||
|
||||
//---------
|
||||
|
||||
|
@ -2373,8 +2501,9 @@ RunTestMode(FILE* fp)
|
|||
// At the end we're 64 bytes into the current sample so we report ~1,424
|
||||
// bytes of allocation overall, which is 64 less than the real value 1,488.
|
||||
|
||||
// Dump 4.
|
||||
Dump(writer);
|
||||
// AnalyzeReports 4.
|
||||
AnalyzeReports(writer);
|
||||
AnalyzeHeap(writer);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -2450,7 +2579,7 @@ RunStressMode(FILE* fp)
|
|||
stress1(); stress1(); stress1(); stress1(); stress1();
|
||||
stress1(); stress1(); stress1(); stress1(); stress1();
|
||||
|
||||
Dump(writer);
|
||||
AnalyzeReports(writer);
|
||||
}
|
||||
|
||||
} // namespace dmd
|
||||
|
|
|
@ -43,22 +43,28 @@ private:
|
|||
// reporters. The following sequence should be used.
|
||||
// - ClearReports()
|
||||
// - run the memory reporters
|
||||
// - Dump()
|
||||
// - AnalyzeReports()
|
||||
// This sequence avoids spurious twice-reported warnings.
|
||||
MOZ_EXPORT void
|
||||
ClearReports();
|
||||
|
||||
// Checks which heap blocks have been reported, and dumps a human-readable
|
||||
// Determines which heap blocks have been reported, and dumps a human-readable
|
||||
// summary (via |aWrite|). If |aWrite| is nullptr it will dump to stderr.
|
||||
// Beware: this output may have very long lines.
|
||||
// Beware: this output may have very long lines.
|
||||
MOZ_EXPORT void
|
||||
Dump(Writer aWriter);
|
||||
AnalyzeReports(const Writer& aWriter);
|
||||
|
||||
// A useful |WriterFun|. If |fp| is a FILE* you want |Dump|'s output to be
|
||||
// written to, call:
|
||||
// Measures all heap blocks, and dumps a human-readable summary (via |aWrite|).
|
||||
// If |aWrite| is nullptr it will dump to stderr. Beware: this output may
|
||||
// have very long lines.
|
||||
MOZ_EXPORT void
|
||||
AnalyzeHeap(const Writer& aWriter);
|
||||
|
||||
// A useful |WriterFun|. For example, if |fp| is a FILE* you want
|
||||
// |AnalyzeReports|'s output to be written to, call:
|
||||
//
|
||||
// dmd::Writer writer(FpWrite, fp);
|
||||
// dmd::Dump(writer);
|
||||
// dmd::AnalyzeReports(writer);
|
||||
MOZ_EXPORT void
|
||||
FpWrite(void* aFp, const char* aFmt, va_list aAp);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeReports
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -30,6 +31,25 @@ Summary {
|
|||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeHeap
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
# no live heap blocks
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: 0 bytes in 0 blocks
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeReports
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -289,6 +309,131 @@ Summary {
|
|||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeHeap
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Live {
|
||||
1 block in heap block record 1 of 12
|
||||
8,192 bytes (4,097 requested / 4,095 slop)
|
||||
67.77% of the heap (67.77% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 2 of 12
|
||||
1,024 bytes (1,023 requested / 1 slop)
|
||||
8.47% of the heap (76.24% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
9 blocks in heap block record 3 of 12
|
||||
1,008 bytes (900 requested / 108 slop)
|
||||
8.34% of the heap (84.58% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
6 blocks in heap block record 4 of 12
|
||||
528 bytes (528 requested / 0 slop)
|
||||
4.37% of the heap (88.95% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
6 blocks in heap block record 5 of 12
|
||||
528 bytes (528 requested / 0 slop)
|
||||
4.37% of the heap (93.32% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 6 of 12
|
||||
512 bytes (512 requested / 0 slop)
|
||||
4.24% of the heap (97.55% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 7 of 12
|
||||
80 bytes (79 requested / 1 slop)
|
||||
0.66% of the heap (98.21% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 8 of 12
|
||||
80 bytes (78 requested / 2 slop)
|
||||
0.66% of the heap (98.87% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 9 of 12
|
||||
80 bytes (77 requested / 3 slop)
|
||||
0.66% of the heap (99.54% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 10 of 12
|
||||
32 bytes (30 requested / 2 slop)
|
||||
0.26% of the heap (99.80% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 11 of 12
|
||||
16 bytes (10 requested / 6 slop)
|
||||
0.13% of the heap (99.93% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 12 of 12
|
||||
8 bytes (0 requested / 8 slop)
|
||||
0.07% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: 12,088 bytes in 30 blocks
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeReports
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -425,6 +570,104 @@ Summary {
|
|||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeHeap
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Live {
|
||||
9 blocks in heap block record 1 of 9
|
||||
1,008 bytes (900 requested / 108 slop)
|
||||
35.49% of the heap (35.49% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
6 blocks in heap block record 2 of 9
|
||||
528 bytes (528 requested / 0 slop)
|
||||
18.59% of the heap (54.08% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
6 blocks in heap block record 3 of 9
|
||||
528 bytes (528 requested / 0 slop)
|
||||
18.59% of the heap (72.68% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 4 of 9
|
||||
512 bytes (512 requested / 0 slop)
|
||||
18.03% of the heap (90.70% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 5 of 9
|
||||
80 bytes (79 requested / 1 slop)
|
||||
2.82% of the heap (93.52% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 6 of 9
|
||||
80 bytes (78 requested / 2 slop)
|
||||
2.82% of the heap (96.34% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 7 of 9
|
||||
80 bytes (77 requested / 3 slop)
|
||||
2.82% of the heap (99.15% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 8 of 9
|
||||
16 bytes (10 requested / 6 slop)
|
||||
0.56% of the heap (99.72% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 9 of 9
|
||||
8 bytes (0 requested / 8 slop)
|
||||
0.28% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: 2,840 bytes in 27 blocks
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeReports
|
||||
Sample-below size = 128
|
||||
}
|
||||
|
||||
|
@ -517,3 +760,82 @@ Summary {
|
|||
Twice-reported: ~0 bytes ( 0.00%) in ~0 blocks ( 0.00%)
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
Function = AnalyzeHeap
|
||||
Sample-below size = 128
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Live {
|
||||
~4 blocks in heap block record 1 of 7
|
||||
~512 bytes (~512 requested / ~0 slop)
|
||||
35.96% of the heap (35.96% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 2 of 7
|
||||
256 bytes (256 requested / 0 slop)
|
||||
17.98% of the heap (53.93% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 3 of 7
|
||||
144 bytes (144 requested / 0 slop)
|
||||
10.11% of the heap (64.04% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
1 block in heap block record 4 of 7
|
||||
128 bytes (128 requested / 0 slop)
|
||||
8.99% of the heap (73.03% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
~1 block in heap block record 5 of 7
|
||||
~128 bytes (~128 requested / ~0 slop)
|
||||
8.99% of the heap (82.02% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
~1 block in heap block record 6 of 7
|
||||
~128 bytes (~128 requested / ~0 slop)
|
||||
8.99% of the heap (91.01% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
~1 block in heap block record 7 of 7
|
||||
~128 bytes (~128 requested / ~0 slop)
|
||||
8.99% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
... DMD.cpp
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: ~1,424 bytes in ~10 blocks
|
||||
}
|
||||
|
||||
|
|
|
@ -93,11 +93,20 @@ MemoryStats.dump = function (logger,
|
|||
}, null, /* anonymize = */ false);
|
||||
}
|
||||
|
||||
// This is the old, deprecated function.
|
||||
if (dumpDMD && typeof(DMDReportAndDump) != undefined) {
|
||||
var basename = "dmd-" + testNumber + "-deprecated.txt";
|
||||
var dumpfile = MemoryStats.constructPathname(dumpOutputDirectory,
|
||||
basename);
|
||||
logger.info(testURL + " | DMD-DUMP-deprecated " + dumpfile);
|
||||
DMDReportAndDump(dumpfile);
|
||||
}
|
||||
|
||||
if (dumpDMD && typeof(DMDAnalyzeReports) != undefined) {
|
||||
var basename = "dmd-" + testNumber + ".txt";
|
||||
var dumpfile = MemoryStats.constructPathname(dumpOutputDirectory,
|
||||
basename);
|
||||
logger.info(testURL + " | DMD-DUMP " + dumpfile);
|
||||
DMDReportAndDump(dumpfile);
|
||||
DMDAnalyzeReports(dumpfile);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -53,7 +53,8 @@ const gUnnamedProcessStr = "Main Process";
|
|||
|
||||
let gIsDiff = false;
|
||||
|
||||
const DMDFile = "out.dmd";
|
||||
const gAnalyzeReportsFile = "reports.dmd";
|
||||
const gAnalyzeHeapFile = "heap.dmd";
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -298,7 +299,10 @@ function onLoad()
|
|||
"collection log.\n" +
|
||||
"WARNING: These logs may be large (>1GB).";
|
||||
|
||||
const DMDEnabledDesc = "Run DMD analysis and save it to '" + DMDFile + "'.\n";
|
||||
const AnalyzeReportsDesc = "Analyze memory reports coverage and save the "
|
||||
"output to '" + gAnalyzeReportsFile + "'.\n";
|
||||
const AnalyzeHeapDesc = "Analyze heap usage and save the output to '" +
|
||||
gAnalyzeHeapFile + "'.\n";
|
||||
const DMDDisabledDesc = "DMD is not running. Please re-start with $DMD and " +
|
||||
"the other relevant environment variables set " +
|
||||
"appropriately.";
|
||||
|
@ -359,12 +363,20 @@ function onLoad()
|
|||
if (gMgr.isDMDEnabled) {
|
||||
let row5 = appendElement(ops, "div", "opsRow");
|
||||
|
||||
appendElementWithText(row5, "div", "opsRowLabel", "Save DMD output");
|
||||
let enableButton = gMgr.isDMDRunning;
|
||||
let dmdButton =
|
||||
appendButton(row5, enableButton ? DMDEnabledDesc : DMDDisabledDesc,
|
||||
doDMD, "Save", "dmdButton");
|
||||
dmdButton.disabled = !enableButton;
|
||||
appendElementWithText(row5, "div", "opsRowLabel", "DMD operations");
|
||||
let enableButtons = gMgr.isDMDRunning;
|
||||
|
||||
let analyzeReportsButton =
|
||||
appendButton(row5,
|
||||
enableButtons ? AnalyzeReportsDesc : DMDDisabledDesc,
|
||||
doAnalyzeReports, "Analyze reports");
|
||||
analyzeReportsButton.disabled = !enableButtons;
|
||||
|
||||
let analyzeHeapButton =
|
||||
appendButton(row5,
|
||||
enableButtons ? AnalyzeHeapDesc : DMDDisabledDesc,
|
||||
doAnalyzeHeap, "Analyze heap");
|
||||
analyzeHeapButton.disabled = !enableButtons;
|
||||
}
|
||||
|
||||
// Generate the main div, where content ("section" divs) will go. It's
|
||||
|
@ -445,12 +457,24 @@ function saveGCLogAndVerboseCCLog()
|
|||
dumpGCLogAndCCLog(true);
|
||||
}
|
||||
|
||||
function doDMD()
|
||||
function doAnalyzeReports()
|
||||
{
|
||||
updateMainAndFooter('Saving DMD output...', HIDE_FOOTER);
|
||||
try {
|
||||
let x = DMDReportAndDump('out.dmd');
|
||||
updateMainAndFooter('Saved DMD output to ' + DMDFile, HIDE_FOOTER);
|
||||
let x = DMDAnalyzeReports(gAnalyzeReportsFile);
|
||||
updateMainAndFooter('Saved DMD output to ' + gAnalyzeReportsFile,
|
||||
HIDE_FOOTER);
|
||||
} catch (ex) {
|
||||
updateMainAndFooter(ex.toString(), HIDE_FOOTER);
|
||||
}
|
||||
}
|
||||
|
||||
function doAnalyzeHeap()
|
||||
{
|
||||
updateMainAndFooter('Saving DMD output...', HIDE_FOOTER);
|
||||
try {
|
||||
let x = DMDAnalyzeHeap(gAnalyzeHeapFile);
|
||||
updateMainAndFooter('Saved DMD output to ' + gAnalyzeHeapFile, HIDE_FOOTER);
|
||||
} catch (ex) {
|
||||
updateMainAndFooter(ex.toString(), HIDE_FOOTER);
|
||||
}
|
||||
|
|
|
@ -721,10 +721,10 @@ nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
|
|||
return rv;
|
||||
}
|
||||
|
||||
// Dump DMD output to the file.
|
||||
// Dump DMD's memory reports analysis to the file.
|
||||
DMDWriteState state(dmdWriter);
|
||||
dmd::Writer w(DMDWrite, &state);
|
||||
dmd::Dump(w);
|
||||
dmd::AnalyzeReports(w);
|
||||
|
||||
rv = dmdWriter->Finish();
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
|
|
Загрузка…
Ссылка в новой задаче