Bug 1094552 (part 5) - DMD: choose the profiling mode at start-up. r=mccr8.

This patch moves profiling mode selection from post-processing (in dmd.py) to
DMD start-up. This will make it easier to add new kinds of profiling, such as
cumulative heap profiling.

Specifically:

- There's a new --mode option. |LiveWithReports| is the default, as it is
  currently.

- dmd.py's --ignore-reports option is gone.

- There's a new |mode| field in the JSON output.

- Reports-related operations are now no-ops if DMD isn't in LiveWithReports
  mode.

- Diffs are only allowed for output files that have the same mode.

- A new function ResetEverything() replaces the SetSampleBelowSize() and
  ClearBlocks(), which were used by the test to change DMD options.

- The tests in SmokeDMD.cpp are split up so they can be run multiple times, in
  different modes. The exact combinations of tests and modes has been changed a
  bit.

--HG--
rename : memory/replace/dmd/test/full-reports-empty-expected.txt => memory/replace/dmd/test/full-empty-dark-matter-expected.txt
rename : memory/replace/dmd/test/full-heap-empty-expected.txt => memory/replace/dmd/test/full-empty-live-expected.txt
rename : memory/replace/dmd/test/full-heap-sampled-expected.txt => memory/replace/dmd/test/full-sampled-live-expected.txt
rename : memory/replace/dmd/test/full-reports-unsampled1-expected.txt => memory/replace/dmd/test/full-unsampled1-dark-matter-expected.txt
rename : memory/replace/dmd/test/full-heap-unsampled1-expected.txt => memory/replace/dmd/test/full-unsampled1-live-expected.txt
rename : memory/replace/dmd/test/full-reports-unsampled2-expected.txt => memory/replace/dmd/test/full-unsampled2-dark-matter-expected.txt
rename : memory/replace/dmd/test/script-diff-basic-expected.txt => memory/replace/dmd/test/script-diff-live-expected.txt
rename : memory/replace/dmd/test/script-diff1.json => memory/replace/dmd/test/script-diff-live1.json
rename : memory/replace/dmd/test/script-diff2.json => memory/replace/dmd/test/script-diff-live2.json
extra : rebase_source : bf32cc4e0d82aa1a20ceb55e8ea259850b49cc06
This commit is contained in:
Nicholas Nethercote 2014-12-08 14:45:14 -08:00
parent d1bf866e67
commit 5b5d3c27bb
31 changed files with 497 additions and 433 deletions

View File

@ -305,9 +305,44 @@ class Options
{}
};
// DMD has several modes. These modes affect what data is recorded and
// written to the output file, and the written data affects the
// post-processing that dmd.py can do.
//
// Users specify the mode as soon as DMD starts. This leads to minimal memory
// usage and log file size. It has the disadvantage that is inflexible -- if
// you want to change modes you have to re-run DMD. But in practice changing
// modes seems to be rare, so it's not much of a problem.
//
// An alternative possibility would be to always record and output *all* the
// information needed for all modes. This would let you choose the mode when
// running dmd.py, and so you could do multiple kinds of profiling on a
// single DMD run. But if you are only interested in one of the simpler
// modes, you'd pay the price of (a) increased memory usage and (b) *very*
// large log files.
//
// Finally, another alternative possibility would be to do mode selection
// partly at DMD startup or recording, and then partly in dmd.py. This would
// give some extra flexibility at moderate memory and file size cost. But
// certain mode pairs wouldn't work, which would be confusing.
//
enum Mode
{
// For each live block, this mode outputs: size (usable and slop),
// allocation stack, and whether it's sampled. This mode is good for live
// heap profiling.
Live,
// 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
};
char* mDMDEnvVar; // a saved copy, for later printing
NumOption<size_t> mSampleBelowSize;
Mode mMode;
NumOption<size_t> mSampleBelowSize;
NumOption<uint32_t> mMaxFrames;
bool mShowDumpStats;
@ -320,13 +355,14 @@ class Options
public:
explicit Options(const char* aDMDEnvVar);
bool IsLiveMode() const { return mMode == Live; }
bool IsDarkMatterMode() const { return mMode == DarkMatter; }
const char* DMDEnvVar() const { return mDMDEnvVar; }
size_t SampleBelowSize() const { return mSampleBelowSize.mActual; }
size_t MaxFrames() const { return mMaxFrames.mActual; }
size_t ShowDumpStats() const { return mShowDumpStats; }
void SetSampleBelowSize(size_t aSize) { mSampleBelowSize.mActual = aSize; }
};
static Options *gOptions;
@ -401,8 +437,11 @@ public:
bool IsLocked() { return mIsLocked; }
};
// This lock must be held while manipulating global state, such as
// gStackTraceTable, gLiveBlockTable, etc.
// 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.
static Mutex* gStateLock = nullptr;
class AutoLockState
@ -780,7 +819,8 @@ public:
bool Tag() const { return bool(mUint & kTagMask); }
};
// A live heap block.
// A live heap block. Stores both basic data and data about reports, if we're
// in DarkMatter mode.
class LiveBlock
{
const void* mPtr;
@ -788,6 +828,8 @@ class LiveBlock
// Ptr: |mAllocStackTrace| - stack trace where this block was allocated.
// Tag bit 0: |mIsSampled| - was this block sampled? (if so, slop == 0).
//
// Only used in DarkMatter mode.
TaggedPtr<const StackTrace* const>
mAllocStackTrace_mIsSampled;
@ -801,6 +843,8 @@ class LiveBlock
//
// |mPtr| is used as the key in LiveBlockTable, so it's ok for this member
// to be |mutable|.
//
// Only used in DarkMatter mode.
mutable TaggedPtr<const StackTrace*> mReportStackTrace_mReportedOnAlloc[2];
public:
@ -841,38 +885,45 @@ public:
const StackTrace* ReportStackTrace1() const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
return mReportStackTrace_mReportedOnAlloc[0].Ptr();
}
const StackTrace* ReportStackTrace2() const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
return mReportStackTrace_mReportedOnAlloc[1].Ptr();
}
bool ReportedOnAlloc1() const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
return mReportStackTrace_mReportedOnAlloc[0].Tag();
}
bool ReportedOnAlloc2() const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
return mReportStackTrace_mReportedOnAlloc[1].Tag();
}
void AddStackTracesToTable(StackTraceSet& aStackTraces) const
{
aStackTraces.put(AllocStackTrace()); // never null
const StackTrace* st;
if ((st = ReportStackTrace1())) { // may be null
aStackTraces.put(st);
}
if ((st = ReportStackTrace2())) { // may be null
aStackTraces.put(st);
if (gOptions->IsDarkMatterMode()) {
const StackTrace* st;
if ((st = ReportStackTrace1())) { // may be null
aStackTraces.put(st);
}
if ((st = ReportStackTrace2())) { // may be null
aStackTraces.put(st);
}
}
}
uint32_t NumReports() const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
if (ReportStackTrace2()) {
MOZ_ASSERT(ReportStackTrace1());
return 2;
@ -886,6 +937,7 @@ public:
// This is |const| thanks to the |mutable| fields above.
void Report(Thread* aT, bool aReportedOnAlloc) const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
// We don't bother recording reports after the 2nd one.
uint32_t numReports = NumReports();
if (numReports < 2) {
@ -896,6 +948,7 @@ public:
void UnreportIfNotReportedOnAlloc() const
{
MOZ_ASSERT(gOptions->IsDarkMatterMode());
if (!ReportedOnAlloc1() && !ReportedOnAlloc2()) {
mReportStackTrace_mReportedOnAlloc[0].Set(nullptr, 0);
mReportStackTrace_mReportedOnAlloc[1].Set(nullptr, 0);
@ -1236,10 +1289,11 @@ Options::GetBool(const char* aArg, const char* aOptionName, bool* aValue)
// prime size will explore all possible values of the alloc counter.
//
Options::Options(const char* aDMDEnvVar)
: mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar)),
mSampleBelowSize(4093, 100 * 100 * 1000),
mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames),
mShowDumpStats(false)
: mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar))
, mMode(DarkMatter)
, mSampleBelowSize(4093, 100 * 100 * 1000)
, mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames)
, mShowDumpStats(false)
{
char* e = mDMDEnvVar;
if (strcmp(e, "1") != 0) {
@ -1265,7 +1319,13 @@ Options::Options(const char* aDMDEnvVar)
// Handle arg
long myLong;
bool myBool;
if (GetLong(arg, "--sample-below", 1, mSampleBelowSize.mMax, &myLong)) {
if (strcmp(arg, "--mode=live") == 0) {
mMode = Options::Live;
} else if (strcmp(arg, "--mode=dark-matter") == 0) {
mMode = Options::DarkMatter;
} else if (GetLong(arg, "--sample-below", 1, mSampleBelowSize.mMax,
&myLong)) {
mSampleBelowSize.mActual = myLong;
} else if (GetLong(arg, "--max-frames", 1, mMaxFrames.mMax, &myLong)) {
@ -1298,6 +1358,8 @@ Options::BadArg(const char* aArg)
StatusMsg("entries.\n");
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(" --sample-below=<1..%d> Sample blocks smaller than this [%d]\n",
int(mSampleBelowSize.mMax),
int(mSampleBelowSize.mDefault));
@ -1374,13 +1436,13 @@ Init(const malloc_table_t* aMallocTable)
}
//---------------------------------------------------------------------------
// DMD reporting and unreporting
// Block reporting and unreporting
//---------------------------------------------------------------------------
static void
ReportHelper(const void* aPtr, bool aReportedOnAlloc)
{
if (!aPtr) {
if (!gOptions->IsDarkMatterMode() || !aPtr) {
return;
}
@ -1416,12 +1478,9 @@ DMDFuncs::ReportOnAlloc(const void* aPtr)
//---------------------------------------------------------------------------
// The version number of the output format. Increment this if you make
// backwards-incompatible changes to the format.
//
// Version history:
// - 1: The original format (bug 1044709).
//
static const int kOutputVersionNumber = 1;
// backwards-incompatible changes to the format. See DMD.h for the version
// history.
static const int kOutputVersionNumber = 2;
// Note that, unlike most SizeOf* functions, this function does not take a
// |mozilla::MallocSizeOf| argument. That's because those arguments are
@ -1472,6 +1531,10 @@ DMDFuncs::SizeOf(Sizes* aSizes)
void
DMDFuncs::ClearReports()
{
if (!gOptions->IsDarkMatterMode()) {
return;
}
AutoLockState lock;
// Unreport all blocks that were marked reported by a memory reporter. This
@ -1487,8 +1550,8 @@ class ToIdStringConverter MOZ_FINAL
public:
ToIdStringConverter() : mNextId(0) { mIdMap.init(512); }
// Converts a pointer to a unique ID. Reuses the existing ID for the pointer if
// it's been seen before.
// Converts a pointer to a unique ID. Reuses the existing ID for the pointer
// if it's been seen before.
const char* ToIdString(const void* aPtr)
{
uint32_t id;
@ -1576,6 +1639,16 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
writer.StartObjectProperty("invocation");
{
writer.StringProperty("dmdEnvVar", gOptions->DMDEnvVar());
const char* mode;
if (gOptions->IsLiveMode()) {
mode = "live";
} else if (gOptions->IsDarkMatterMode()) {
mode = "dark-matter";
} else {
MOZ_ASSERT(false);
mode = "(unknown DMD mode)";
}
writer.StringProperty("mode", mode);
writer.IntProperty("sampleBelowSize", gOptions->SampleBelowSize());
}
writer.EndObject();
@ -1599,7 +1672,8 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWriter)
}
}
writer.StringProperty("alloc", isc.ToIdString(b.AllocStackTrace()));
if (b.NumReports() > 0) {
if (gOptions->IsDarkMatterMode() && b.NumReports() > 0) {
MOZ_ASSERT(gOptions->IsDarkMatterMode());
writer.StartArrayProperty("reps");
{
if (b.ReportStackTrace1()) {
@ -1735,14 +1809,15 @@ DMDFuncs::Analyze(UniquePtr<JSONWriteFunc> aWriter)
//---------------------------------------------------------------------------
void
DMDFuncs::SetSampleBelowSize(size_t aSize)
DMDFuncs::ResetEverything(const char* aOptions)
{
gOptions->SetSampleBelowSize(aSize);
}
AutoLockState lock;
void
DMDFuncs::ClearBlocks()
{
// Reset options.
InfallibleAllocPolicy::delete_(gOptions);
gOptions = InfallibleAllocPolicy::new_<Options>(aOptions);
// Clear all existing blocks.
gLiveBlockTable->clear();
gSmallBlockActualSizeCounter = 0;
}

View File

@ -51,9 +51,7 @@ struct DMDFuncs
virtual void StatusMsg(const char*, va_list);
virtual void SetSampleBelowSize(size_t);
virtual void ClearBlocks();
virtual void ResetEverything(const char*);
#ifndef REPLACE_MALLOC_IMPL
// We deliberately don't use ReplaceMalloc::GetDMDFuncs here, because if we
@ -146,13 +144,21 @@ ClearReports()
// {
// // The version number of the format, which will be incremented each time
// // backwards-incompatible changes are made. A mandatory integer.
// "version": 1,
// //
// // Version history:
// // - 1: The original format. Implemented in bug 1044709.
// // - 2: Added the "mode" field under "invocation". Added in bug 1094552.
// "version": 2,
//
// // Information about how DMD was invoked. A mandatory object.
// "invocation": {
// // The contents of the $DMD environment variable. A mandatory string.
// "dmdEnvVar": "1",
//
// // The profiling mode. A mandatory string taking one of the following
// // values: "live", "dark-matter".
// "mode": "dark-matter",
//
// // The value of the --sample-below-size option. A mandatory integer.
// "sampleBelowSize": 4093
// },
@ -182,8 +188,9 @@ ClearReports()
// "alloc": "B",
//
// // One or more stack traces at which this heap block was reported by a
// // memory reporter. An optional array. The elements are strings that
// // index into the "traceTable" object.
// // memory reporter. An optional array that will only be present in
// // "dark-matter" mode. The elements are strings that index into
// // the "traceTable" object.
// "reps": ["C"]
// }
// ],
@ -221,11 +228,13 @@ ClearReports()
// "H": "#00: quuux (Quux.cpp:567)"
// }
// }
//
// Implementation note: normally, this wouldn't be templated, but in that case,
// the function is compiled, which makes the destructor for the UniquePtr fire
// up, and that needs JSONWriteFunc to be fully defined. That, in turn,
// requires to include JSONWriter.h, which includes double-conversion.h, which
// ends up breaking various things built with -Werror for various reasons.
//
template <typename JSONWriteFunc>
inline void
Analyze(UniquePtr<JSONWriteFunc> aWriteFunc)
@ -267,23 +276,15 @@ IsRunning()
return !!DMDFuncs::Get();
}
// Sets the sample-below size. Only used for testing purposes.
// Resets all DMD options and then sets new ones according to those specified
// in |aOptions|. Also clears all recorded data about allocations. Only used
// for testing purposes.
inline void
SetSampleBelowSize(size_t aSize)
ResetEverything(const char* aOptions)
{
DMDFuncs* funcs = DMDFuncs::Get();
if (funcs) {
funcs->SetSampleBelowSize(aSize);
}
}
// Clears all records of live allocations. Only used for testing purposes.
inline void
ClearBlocks()
{
DMDFuncs* funcs = DMDFuncs::Get();
if (funcs) {
funcs->ClearBlocks();
funcs->ResetEverything(aOptions);
}
}
#endif

View File

@ -20,7 +20,7 @@ import sys
import tempfile
# The DMD output version this script handles.
outputVersion = 1
outputVersion = 2
# If --ignore-alloc-fns is specified, stack frames containing functions that
# match these strings will be removed from the *start* of stack traces. (Once
@ -184,10 +184,6 @@ variable is used to find breakpad symbols for stack fixing.
p.add_argument('-f', '--max-frames', type=range_1_24,
help='maximum number of frames to consider in each trace')
p.add_argument('-r', '--ignore-reports', action='store_true',
help='ignore memory reports data; useful if you just ' +
'want basic heap profiling')
p.add_argument('-s', '--sort-by', choices=sortByChoices.keys(),
default=sortByChoices.keys()[0],
help='sort the records by a particular metric')
@ -278,11 +274,15 @@ def getDigestFromFile(args, inputFile):
# Extract the main parts of the JSON object.
invocation = j['invocation']
dmdEnvVar = invocation['dmdEnvVar']
mode = invocation['mode']
sampleBelowSize = invocation['sampleBelowSize']
blockList = j['blockList']
traceTable = j['traceTable']
frameTable = j['frameTable']
if not mode in ['live', 'dark-matter']:
raise Exception("bad 'mode' property: '{:s}'".format(mode))
heapIsSampled = sampleBelowSize > 1 # is sampling present?
# Remove allocation functions at the start of traces.
@ -336,9 +336,9 @@ def getDigestFromFile(args, inputFile):
# Aggregate blocks into records. All sufficiently similar blocks go into a
# single record.
if args.ignore_reports:
if mode == 'live':
liveRecords = collections.defaultdict(Record)
else:
elif mode == 'dark-matter':
unreportedRecords = collections.defaultdict(Record)
onceReportedRecords = collections.defaultdict(Record)
twiceReportedRecords = collections.defaultdict(Record)
@ -369,10 +369,10 @@ def getDigestFromFile(args, inputFile):
traceTable[traceKey]))
allocatedAtTraceKey = block['alloc']
if args.ignore_reports:
if mode == 'live':
recordKey = makeRecordKeyPart(allocatedAtTraceKey)
records = liveRecords
else:
elif mode == 'dark-matter':
recordKey = makeRecordKeyPart(allocatedAtTraceKey)
if 'reps' in block:
reportedAtTraceKeys = block['reps']
@ -414,9 +414,9 @@ def getDigestFromFile(args, inputFile):
buildTraceDescription(traceTable, frameTable,
allocatedAtTraceKey)
if args.ignore_reports:
if mode == 'live':
pass
else:
elif mode == 'dark-matter':
if 'reps' in block and record.reportedAtDescs == []:
f = lambda k: buildTraceDescription(traceTable, frameTable, k)
record.reportedAtDescs = map(f, reportedAtTraceKeys)
@ -425,13 +425,14 @@ def getDigestFromFile(args, inputFile):
# All the processed data for a single DMD file is called a "digest".
digest = {}
digest['dmdEnvVar'] = dmdEnvVar
digest['mode'] = mode
digest['sampleBelowSize'] = sampleBelowSize
digest['heapUsableSize'] = heapUsableSize
digest['heapBlocks'] = heapBlocks
digest['heapIsSampled'] = heapIsSampled
if args.ignore_reports:
if mode == 'live':
digest['liveRecords'] = liveRecords
else:
elif mode == 'dark-matter':
digest['unreportedRecords'] = unreportedRecords
digest['onceReportedRecords'] = onceReportedRecords
digest['twiceReportedRecords'] = twiceReportedRecords
@ -464,16 +465,20 @@ def diffRecords(args, records1, records2):
def diffDigests(args, d1, d2):
if (d1['mode'] != d2['mode']):
raise Exception("the input files have different 'mode' properties")
d3 = {}
d3['dmdEnvVar'] = (d1['dmdEnvVar'], d2['dmdEnvVar'])
d3['mode'] = d1['mode']
d3['sampleBelowSize'] = (d1['sampleBelowSize'], d2['sampleBelowSize'])
d3['heapUsableSize'] = d2['heapUsableSize'] - d1['heapUsableSize']
d3['heapBlocks'] = d2['heapBlocks'] - d1['heapBlocks']
d3['heapIsSampled'] = d2['heapIsSampled'] or d1['heapIsSampled']
if args.ignore_reports:
if d1['mode'] == 'live':
d3['liveRecords'] = diffRecords(args, d1['liveRecords'],
d2['liveRecords'])
else:
elif d1['mode'] == 'dark-matter':
d3['unreportedRecords'] = diffRecords(args, d1['unreportedRecords'],
d2['unreportedRecords'])
d3['onceReportedRecords'] = diffRecords(args, d1['onceReportedRecords'],
@ -485,13 +490,14 @@ def diffDigests(args, d1, d2):
def printDigest(args, digest):
dmdEnvVar = digest['dmdEnvVar']
mode = digest['mode']
sampleBelowSize = digest['sampleBelowSize']
heapUsableSize = digest['heapUsableSize']
heapIsSampled = digest['heapIsSampled']
heapBlocks = digest['heapBlocks']
if args.ignore_reports:
if mode == 'live':
liveRecords = digest['liveRecords']
else:
elif mode == 'dark-matter':
unreportedRecords = digest['unreportedRecords']
onceReportedRecords = digest['onceReportedRecords']
twiceReportedRecords = digest['twiceReportedRecords']
@ -583,9 +589,9 @@ def printDigest(args, digest):
out(' {:4.2f}% of the heap ({:4.2f}% cumulative)'.
format(perc(record.usableSize, heapUsableSize),
perc(kindCumulativeUsableSize, heapUsableSize)))
if args.ignore_reports:
if mode == 'live':
pass
else:
elif mode == 'dark-matter':
out(' {:4.2f}% of {:} ({:4.2f}% cumulative)'.
format(perc(record.usableSize, kindUsableSize),
recordKind,
@ -593,9 +599,9 @@ def printDigest(args, digest):
out(' Allocated at {')
printStack(record.allocatedAtDesc)
out(' }')
if args.ignore_reports:
if mode == 'live':
pass
else:
elif mode == 'dark-matter':
for n, reportedAtDesc in enumerate(record.reportedAtDescs):
again = 'again ' if n > 0 else ''
out(' Reported {:}at {{'.format(again))
@ -625,10 +631,10 @@ def printDigest(args, digest):
printInvocation(' 2', dmdEnvVar[1], sampleBelowSize[1])
# Print records.
if args.ignore_reports:
if mode == 'live':
liveUsableSize, liveBlocks = \
printRecords('live', liveRecords, heapUsableSize)
else:
elif mode == 'dark-matter':
twiceReportedUsableSize, twiceReportedBlocks = \
printRecords('twice-reported', twiceReportedRecords, heapUsableSize)
@ -641,11 +647,11 @@ def printDigest(args, digest):
# Print summary.
out(separator)
out('Summary {')
if args.ignore_reports:
if mode == 'live':
out(' Total: {:} bytes in {:} blocks'.
format(number(liveUsableSize, heapIsSampled),
number(liveBlocks, heapIsSampled)))
else:
elif mode == 'dark-matter':
fmt = ' {:15} {:>12} bytes ({:6.2f}%) in {:>7} blocks ({:6.2f}%)'
out(fmt.
format('Total:',

View File

@ -89,165 +89,160 @@ void Foo(int aSeven)
}
void
RunTests()
TestEmpty(const char* aTestName, const char* aMode)
{
// These files are written to $CWD.
auto f1 = MakeUnique<FpWriteFunc>("full-empty.json");
auto f2 = MakeUnique<FpWriteFunc>("full-unsampled1.json");
auto f3 = MakeUnique<FpWriteFunc>("full-unsampled2.json");
auto f4 = MakeUnique<FpWriteFunc>("full-sampled.json");
char filename[128];
sprintf(filename, "full-%s-%s.json", aTestName, aMode);
auto f = MakeUnique<FpWriteFunc>(filename);
// This test relies on the compiler not doing various optimizations, such as
// eliding unused malloc() calls or unrolling loops with fixed iteration
// counts. So we compile it with -O0 (or equivalent), which probably prevents
// that. We also use the following variable for various loop iteration
// counts, just in case compilers might unroll very small loops even with
// -O0.
int seven = 7;
char options[128];
sprintf(options, "--mode=%s --sample-below=1", aMode);
ResetEverything(options);
// Make sure that DMD is actually running; it is initialized on the first
// allocation.
int *x = (int*)malloc(100);
UseItOrLoseIt(x, seven);
MOZ_RELEASE_ASSERT(IsRunning());
// Zero for everything.
Analyze(Move(f));
}
// The first part of this test requires sampling to be disabled.
SetSampleBelowSize(1);
void
TestUnsampled(const char* aTestName, int aNum, const char* aMode, int aSeven)
{
char filename[128];
sprintf(filename, "full-%s%d-%s.json", aTestName, aNum, aMode);
auto f = MakeUnique<FpWriteFunc>(filename);
// The file manipulations above may have done some heap allocations.
// Clear all knowledge of existing blocks to give us a clean slate.
ClearBlocks();
// The --show-dump-stats=yes is there just to give that option some basic
// testing, e.g. ensure it doesn't crash. It's hard to test much beyond that.
char options[128];
sprintf(options, "--mode=%s --sample-below=1 --show-dump-stats=yes", aMode);
ResetEverything(options);
//---------
// Analyze 1. Zero for everything.
Analyze(Move(f1));
//---------
// Analyze 2: 1 freed, 9 out of 10 unreported.
// Analyze 3: still present and unreported.
// Analyze 1: 1 freed, 9 out of 10 unreported.
// Analyze 2: still present and unreported.
int i;
char* a = nullptr;
for (i = 0; i < seven + 3; i++) {
for (i = 0; i < aSeven + 3; i++) {
a = (char*) malloc(100);
UseItOrLoseIt(a, seven);
UseItOrLoseIt(a, aSeven);
}
free(a);
// Note: 8 bytes is the smallest requested size that gives consistent
// behaviour across all platforms with jemalloc.
// Analyze 2: reported.
// Analyze 3: thrice-reported.
// Analyze 1: reported.
// Analyze 2: thrice-reported.
char* a2 = (char*) malloc(8);
Report(a2);
// Analyze 2: reported.
// Analyze 3: reportedness carries over, due to ReportOnAlloc.
// Analyze 1: reported.
// Analyze 2: reportedness carries over, due to ReportOnAlloc.
char* b = (char*) malloc(10);
ReportOnAlloc(b);
// ReportOnAlloc, then freed.
// Analyze 1: freed, irrelevant.
// Analyze 2: freed, irrelevant.
// Analyze 3: freed, irrelevant.
char* b2 = (char*) malloc(1);
ReportOnAlloc(b2);
free(b2);
// Analyze 2: reported 4 times.
// Analyze 3: freed, irrelevant.
// Analyze 1: reported 4 times.
// Analyze 2: freed, irrelevant.
char* c = (char*) calloc(10, 3);
Report(c);
for (int i = 0; i < seven - 4; i++) {
for (int i = 0; i < aSeven - 4; i++) {
Report(c);
}
// Analyze 2: ignored.
// Analyze 3: irrelevant.
// Analyze 1: ignored.
// Analyze 2: irrelevant.
Report((void*)(intptr_t)i);
// jemalloc rounds this up to 8192.
// Analyze 2: reported.
// Analyze 3: freed.
// Analyze 1: reported.
// Analyze 2: freed.
char* e = (char*) malloc(4096);
e = (char*) realloc(e, 4097);
Report(e);
// First realloc is like malloc; second realloc is shrinking.
// Analyze 2: reported.
// Analyze 3: re-reported.
// Analyze 1: reported.
// Analyze 2: 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.
// Analyze 2: reported.
// Analyze 3: freed, irrelevant.
// Analyze 1: reported.
// Analyze 2: freed, irrelevant.
char* e3 = (char*) realloc(nullptr, 1023);
//e3 = (char*) realloc(e3, 0);
MOZ_ASSERT(e3);
Report(e3);
// Analyze 1: freed, irrelevant.
// Analyze 2: freed, irrelevant.
// Analyze 3: freed, irrelevant.
char* f = (char*) malloc(64);
free(f);
char* f1 = (char*) malloc(64);
free(f1);
// Analyze 2: ignored.
// Analyze 3: irrelevant.
// Analyze 1: ignored.
// Analyze 2: irrelevant.
Report((void*)(intptr_t)0x0);
// Analyze 2: mixture of reported and unreported.
// Analyze 3: all unreported.
Foo(seven);
// Analyze 1: mixture of reported and unreported.
// Analyze 2: all unreported.
Foo(aSeven);
// Analyze 1: twice-reported.
// Analyze 2: twice-reported.
// Analyze 3: twice-reported.
char* g1 = (char*) malloc(77);
ReportOnAlloc(g1);
ReportOnAlloc(g1);
// Analyze 2: mixture of reported and unreported.
// Analyze 3: all unreported.
// Analyze 1: mixture of reported and unreported.
// Analyze 2: all unreported.
// Nb: this Foo() call is deliberately not adjacent to the previous one. See
// the comment about adjacent calls in Foo() for more details.
Foo(seven);
Foo(aSeven);
// Analyze 2: twice-reported.
// Analyze 3: once-reported.
// Analyze 1: twice-reported.
// Analyze 2: once-reported.
char* g2 = (char*) malloc(78);
Report(g2);
ReportOnAlloc(g2);
// Analyze 2: twice-reported.
// Analyze 3: once-reported.
// Analyze 1: twice-reported.
// Analyze 2: once-reported.
char* g3 = (char*) malloc(79);
ReportOnAlloc(g3);
Report(g3);
// All the odd-ball ones.
// Analyze 2: all unreported.
// Analyze 3: all freed, irrelevant.
// Analyze 1: all unreported.
// Analyze 2: all freed, irrelevant.
// XXX: no memalign on Mac
//void* w = memalign(64, 65); // rounds up to 128
//UseItOrLoseIt(w, seven);
//UseItOrLoseIt(w, aSeven);
// XXX: posix_memalign doesn't work on B2G
//void* x;
//posix_memalign(&y, 128, 129); // rounds up to 256
//UseItOrLoseIt(x, seven);
//UseItOrLoseIt(x, aSeven);
// XXX: valloc doesn't work on Windows.
//void* y = valloc(1); // rounds up to 4096
//UseItOrLoseIt(y, seven);
//UseItOrLoseIt(y, aSeven);
// XXX: C11 only
//void* z = aligned_alloc(64, 256);
//UseItOrLoseIt(z, seven);
//UseItOrLoseIt(z, aSeven);
// Analyze 2.
Analyze(Move(f2));
if (aNum == 1) {
// Analyze 1.
Analyze(Move(f));
}
ClearReports();
//---------
@ -262,67 +257,102 @@ RunTests()
//free(y);
//free(z);
// Analyze 3.
Analyze(Move(f3));
if (aNum == 2) {
// Analyze 2.
Analyze(Move(f));
}
}
//---------
void
TestSampled(const char* aTestName, const char* aMode, int aSeven)
{
char filename[128];
sprintf(filename, "full-%s-%s.json", aTestName, aMode);
auto f = MakeUnique<FpWriteFunc>(filename);
// The first part of this test requires sampling to be disabled.
SetSampleBelowSize(128);
// Clear all knowledge of existing blocks to give us a clean slate.
ClearBlocks();
char options[128];
sprintf(options, "--mode=%s --sample-below=128", aMode);
ResetEverything(options);
char* s;
// This equals the sample size, and so is reported exactly. It should be
// listed before records of the same size that are sampled.
s = (char*) malloc(128);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
// This exceeds the sample size, and so is reported exactly.
s = (char*) malloc(160);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
// These together constitute exactly one sample.
for (int i = 0; i < seven + 9; i++) {
for (int i = 0; i < aSeven + 9; i++) {
s = (char*) malloc(8);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
}
// These fall 8 bytes short of a full sample.
for (int i = 0; i < seven + 8; i++) {
for (int i = 0; i < aSeven + 8; i++) {
s = (char*) malloc(8);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
}
// This exceeds the sample size, and so is recorded exactly.
s = (char*) malloc(256);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
// This gets more than to a full sample from the |i < seven + 8| loop above.
// This gets more than to a full sample from the |i < aSeven + 8| loop above.
s = (char*) malloc(96);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
// This gets to another full sample.
for (int i = 0; i < seven - 2; i++) {
for (int i = 0; i < aSeven - 2; i++) {
s = (char*) malloc(8);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
}
// This allocates 16, 32, ..., 128 bytes, which results in a heap block
// record that contains a mix of sample and non-sampled blocks, and so should
// be printed with '~' signs.
for (int i = 1; i <= seven + 1; i++) {
for (int i = 1; i <= aSeven + 1; i++) {
s = (char*) malloc(i * 16);
UseItOrLoseIt(s, seven);
UseItOrLoseIt(s, aSeven);
}
// 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.
// Analyze 4.
Analyze(Move(f4));
Analyze(Move(f));
}
void
RunTests()
{
// This test relies on the compiler not doing various optimizations, such as
// eliding unused malloc() calls or unrolling loops with fixed iteration
// counts. So we compile it with -O0 (or equivalent), which probably prevents
// that. We also use the following variable for various loop iteration
// counts, just in case compilers might unroll very small loops even with
// -O0.
int seven = 7;
// Make sure that DMD is actually running; it is initialized on the first
// allocation.
int *x = (int*)malloc(100);
UseItOrLoseIt(x, seven);
MOZ_RELEASE_ASSERT(IsRunning());
// Please keep this in sync with run_test in test_dmd.js.
TestEmpty("empty", "live");
TestEmpty("empty", "dark-matter");
TestUnsampled("unsampled", 1, "live", seven);
TestUnsampled("unsampled", 1, "dark-matter", seven);
TestUnsampled("unsampled", 2, "dark-matter", seven);
TestSampled("sampled", "live", seven);
}
int main()

View File

@ -1,8 +1,8 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-reports-empty-actual.txt full-empty.json
# dmd.py --filter-stacks-for-testing -o full-empty-dark-matter-actual.txt full-empty-dark-matter.json
Invocation {
$DMD = '1'
$DMD = '--mode=dark-matter --sample-below=1'
Sample-below size = 1
}

View File

@ -1,8 +1,8 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-heap-empty-actual.txt --ignore-reports full-empty.json
# dmd.py --filter-stacks-for-testing -o full-empty-live-actual.txt full-empty-live.json
Invocation {
$DMD = '1'
$DMD = '--mode=live --sample-below=1'
Sample-below size = 1
}

View File

@ -1,100 +0,0 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-heap-unsampled2-actual.txt --ignore-reports full-unsampled2.json
Invocation {
$DMD = '1'
Sample-below size = 1
}
#-----------------------------------------------------------------
Live {
9 blocks in heap block record 1 of 9
1,008 bytes (900 requested / 108 slop)
Individual block sizes: 112 x 9
35.49% of the heap (35.49% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Live {
6 blocks in heap block record 2 of 9
528 bytes (528 requested / 0 slop)
Individual block sizes: 128; 112; 96; 80; 64; 48
18.59% of the heap (54.08% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Live {
6 blocks in heap block record 3 of 9
528 bytes (528 requested / 0 slop)
Individual block sizes: 128; 112; 96; 80; 64; 48
18.59% of the heap (72.68% cumulative)
Allocated at {
#01: ... 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 {
#01: ... 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 {
#01: ... 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 {
#01: ... 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 {
#01: ... 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 {
#01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 9 of 9
8 bytes (8 requested / 0 slop)
0.28% of the heap (100.00% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Summary {
Total: 2,840 bytes in 27 blocks
}

View File

@ -1,98 +0,0 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-reports-sampled-actual.txt full-sampled.json
Invocation {
$DMD = '1'
Sample-below size = 128
}
#-----------------------------------------------------------------
# no twice-reported heap blocks
#-----------------------------------------------------------------
Unreported {
~4 blocks in heap block record 1 of 7
~512 bytes (~512 requested / ~0 slop)
Individual block sizes: ~128 x 3; 128
35.56% of the heap (35.56% cumulative)
35.56% of unreported (35.56% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Unreported {
1 block in heap block record 2 of 7
256 bytes (256 requested / 0 slop)
17.78% of the heap (53.33% cumulative)
17.78% of unreported (53.33% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Unreported {
1 block in heap block record 3 of 7
160 bytes (160 requested / 0 slop)
11.11% of the heap (64.44% cumulative)
11.11% of unreported (64.44% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Unreported {
1 block in heap block record 4 of 7
128 bytes (128 requested / 0 slop)
8.89% of the heap (73.33% cumulative)
8.89% of unreported (73.33% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Unreported {
~1 block in heap block record 5 of 7
~128 bytes (~128 requested / ~0 slop)
8.89% of the heap (82.22% cumulative)
8.89% of unreported (82.22% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Unreported {
~1 block in heap block record 6 of 7
~128 bytes (~128 requested / ~0 slop)
8.89% of the heap (91.11% cumulative)
8.89% of unreported (91.11% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
Unreported {
~1 block in heap block record 7 of 7
~128 bytes (~128 requested / ~0 slop)
8.89% of the heap (100.00% cumulative)
8.89% of unreported (100.00% cumulative)
Allocated at {
#01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
# no once-reported heap blocks
#-----------------------------------------------------------------
Summary {
Total: ~1,440 bytes (100.00%) in ~10 blocks (100.00%)
Unreported: ~1,440 bytes (100.00%) in ~10 blocks (100.00%)
Once-reported: ~0 bytes ( 0.00%) in ~0 blocks ( 0.00%)
Twice-reported: ~0 bytes ( 0.00%) in ~0 blocks ( 0.00%)
}

View File

@ -1,8 +1,8 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-heap-sampled-actual.txt --ignore-reports full-sampled.json
# dmd.py --filter-stacks-for-testing -o full-sampled-live-actual.txt full-sampled-live.json
Invocation {
$DMD = '1'
$DMD = '--mode=live --sample-below=128'
Sample-below size = 128
}

View File

@ -1,8 +1,8 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-reports-unsampled1-actual.txt full-unsampled1.json
# dmd.py --filter-stacks-for-testing -o full-unsampled1-dark-matter-actual.txt full-unsampled1-dark-matter.json
Invocation {
$DMD = '1'
$DMD = '--mode=dark-matter --sample-below=1 --show-dump-stats=yes'
Sample-below size = 1
}

View File

@ -1,8 +1,8 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-heap-unsampled1-actual.txt --ignore-reports full-unsampled1.json
# dmd.py --filter-stacks-for-testing -o full-unsampled1-live-actual.txt full-unsampled1-live.json
Invocation {
$DMD = '1'
$DMD = '--mode=live --sample-below=1 --show-dump-stats=yes'
Sample-below size = 1
}

View File

@ -1,8 +1,8 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o full-reports-unsampled2-actual.txt full-unsampled2.json
# dmd.py --filter-stacks-for-testing -o full-unsampled2-dark-matter-actual.txt full-unsampled2-dark-matter.json
Invocation {
$DMD = '1'
$DMD = '--mode=dark-matter --sample-below=1 --show-dump-stats=yes'
Sample-below size = 1
}

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-diff-basic-actual.txt script-diff1.json script-diff2.json
# dmd.py --filter-stacks-for-testing -o script-diff-dark-matter-actual.txt script-diff-dark-matter1.json script-diff-dark-matter2.json
Invocation 1 {
$DMD = '--sample-below=127'

View File

@ -1,7 +1,8 @@
{
"version": 1,
"version": 2,
"invocation": {
"dmdEnvVar": "--sample-below=127",
"mode": "dark-matter",
"sampleBelowSize": 127
},
"blockList": [

View File

@ -1,7 +1,8 @@
{
"version": 1,
"version": 2,
"invocation": {
"dmdEnvVar": "--sample-below=63",
"mode": "dark-matter",
"sampleBelowSize": 63
},
"blockList": [

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-diff-options-actual.txt --ignore-reports script-diff1.json script-diff2.json
# dmd.py --filter-stacks-for-testing -o script-diff-live-actual.txt script-diff-live1.json script-diff-live2.json
Invocation 1 {
$DMD = '--sample-below=127'

View File

@ -0,0 +1,63 @@
{
"version": 2,
"invocation": {
"dmdEnvVar": "--sample-below=127",
"mode": "live",
"sampleBelowSize": 127
},
"blockList": [
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "B"},
{"req": 4096, "alloc": "B"},
{"req": 4096, "alloc": "B"},
{"req": 4096, "alloc": "B"},
{"req": 4096, "alloc": "C"},
{"req": 4096, "alloc": "C"},
{"req": 4096, "alloc": "C"},
{"req": 4096, "alloc": "C"},
{"req": 4096, "alloc": "D"},
{"req": 4096, "alloc": "D"},
{"req": 2000, "slop": 48, "alloc": "D"},
{"req": 15360, "alloc": "F"},
{"req": 512, "alloc": "F"},
{"req": 512, "alloc": "F"},
{ "alloc": "F"},
{"req": 1024, "alloc": "F"},
{ "alloc": "F"},
{"req": 1000, "slop": 24, "alloc": "F"},
{ "alloc": "F"},
{"req": 4096, "alloc": "G"},
{"req": 8192, "alloc": "G"},
{"req": 16384, "alloc": "G"}
],
"traceTable": {
"A": ["AA"],
"B": ["BB"],
"C": ["CC"],
"D": ["DD"],
"E": ["EE"],
"F": ["FF"],
"G": ["GG"],
"R1": ["RR1"],
"R2": ["RR2"]
},
"frameTable": {
"AA": "#00: A (A.cpp:99)",
"BB": "#00: B (B.cpp:99)",
"CC": "#00: C (C.cpp:99)",
"DD": "#00: D (D.cpp:99)",
"EE": "#00: E (E.cpp:99)",
"FF": "#00: F (F.cpp:99)",
"GG": "#00: G (G.cpp:99)",
"RR1": "#00: R1 (R1.cpp:99)",
"RR2": "#00: R2 (R2.cpp:99)"
}
}

View File

@ -0,0 +1,67 @@
{
"version": 2,
"invocation": {
"dmdEnvVar": "--sample-below=63",
"mode": "live",
"sampleBelowSize": 63
},
"blockList": [
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "A"},
{"req": 4096, "alloc": "A"},
{"req": 8192, "alloc": "B"},
{"req": 8192, "alloc": "B"},
{"req": 4000, "slop": 96, "alloc": "C"},
{"req": 4000, "slop": 96, "alloc": "C"},
{"req": 4000, "slop": 96, "alloc": "C"},
{"req": 4000, "slop": 96, "alloc": "C"},
{"req": 4096, "alloc": "E"},
{"req": 4096, "alloc": "E"},
{"req": 4096, "alloc": "E"},
{"req": 4096, "alloc": "E"},
{"req": 2000, "slop": 48, "alloc": "F"},
{"req": 1000, "slop": 24, "alloc": "F"},
{"req": 512, "alloc": "F"},
{"req": 512, "alloc": "F"},
{"req": 512, "alloc": "F"},
{"req": 512, "alloc": "F"},
{"req": 128, "alloc": "F"},
{ "alloc": "F"},
{"req": 64, "alloc": "F"},
{"req": 64, "alloc": "F"},
{"req": 64, "alloc": "F"},
{"req": 64, "alloc": "F"},
{ "alloc": "F"},
{"req": 4096, "alloc": "G"},
{"req": 4096, "alloc": "G"},
{"req": 20480, "alloc": "G"}
],
"traceTable": {
"A": ["AA"],
"B": ["BB"],
"C": ["CC"],
"D": ["DD"],
"E": ["EE"],
"F": ["FF"],
"G": ["GG"],
"R1": ["RR1"],
"R2": ["RR2"]
},
"frameTable": {
"AA": "#00: A (A.cpp:99)",
"BB": "#00: B (B.cpp:99)",
"CC": "#00: C (C.cpp:99)",
"DD": "#00: D (D.cpp:99)",
"EE": "#00: E (E.cpp:99)",
"FF": "#00: F (F.cpp:99)",
"GG": "#00: G (G.cpp:99)",
"RR1": "#00: R1 (R1.cpp:99)",
"RR2": "#00: R2 (R2.cpp:99)"
}
}

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-ignore-alloc-fns-actual.txt --ignore-reports --ignore-alloc-fns script-ignore-alloc-fns.json
# dmd.py --filter-stacks-for-testing -o script-ignore-alloc-fns-actual.txt --ignore-alloc-fns script-ignore-alloc-fns.json
Invocation {
$DMD = '1'

View File

@ -1,7 +1,8 @@
{
"version": 1,
"version": 2,
"invocation": {
"dmdEnvVar": "1",
"mode": "live",
"sampleBelowSize": 2500
},
"blockList": [

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-max-frames-1-actual.txt --ignore-reports --max-frames=1 script-max-frames.json
# dmd.py --filter-stacks-for-testing -o script-max-frames-1-actual.txt --max-frames=1 script-max-frames.json
Invocation {
$DMD = '1'

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-max-frames-3-actual.txt --ignore-reports --max-frames=3 --no-fix-stacks script-max-frames.json
# dmd.py --filter-stacks-for-testing -o script-max-frames-3-actual.txt --max-frames=3 --no-fix-stacks script-max-frames.json
Invocation {
$DMD = '1'

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-max-frames-8-actual.txt --ignore-reports --max-frames=8 script-max-frames.json
# dmd.py --filter-stacks-for-testing -o script-max-frames-8-actual.txt --max-frames=8 script-max-frames.json
Invocation {
$DMD = '1'

View File

@ -1,7 +1,8 @@
{
"version": 1,
"version": 2,
"invocation": {
"dmdEnvVar": "1",
"mode": "live",
"sampleBelowSize": 1
},
"blockList": [

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-sort-by-req-actual.txt --ignore-reports --sort-by=req --no-fix-stacks script-sort-by.json.gz
# dmd.py --filter-stacks-for-testing -o script-sort-by-req-actual.txt --sort-by=req --no-fix-stacks script-sort-by.json.gz
Invocation {
$DMD = '1'

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-sort-by-slop-actual.txt --ignore-reports --sort-by=slop script-sort-by.json.gz
# dmd.py --filter-stacks-for-testing -o script-sort-by-slop-actual.txt --sort-by=slop script-sort-by.json.gz
Invocation {
$DMD = '1'

View File

@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-sort-by-usable-actual.txt --ignore-reports --sort-by=usable script-sort-by.json.gz
# dmd.py --filter-stacks-for-testing -o script-sort-by-usable-actual.txt --sort-by=usable script-sort-by.json.gz
Invocation {
$DMD = '1'

View File

@ -128,15 +128,25 @@ function run_test() {
runProcess(gDmdTestFile, []);
let fullTestNames = ["empty", "unsampled1", "unsampled2", "sampled"];
for (let i = 0; i < fullTestNames.length; i++) {
let name = fullTestNames[i];
jsonFile = FileUtils.getFile("CurWorkD", ["full-" + name + ".json"]);
test("full-heap-" + name, ["--ignore-reports", jsonFile.path])
test("full-reports-" + name, [jsonFile.path])
jsonFile.remove(true);
function test2(aTestName, aMode) {
let name = "full-" + aTestName + "-" + aMode;
jsonFile = FileUtils.getFile("CurWorkD", [name + ".json"]);
test(name, [jsonFile.path]);
jsonFile.remove(true);
}
// Please keep this in sync with RunTests() in SmokeDMD.cpp.
test2("empty", "live");
test2("empty", "dark-matter");
test2("unsampled1", "live");
test2("unsampled1", "dark-matter");
test2("unsampled2", "dark-matter");
test2("sampled", "live");
// These tests only test the post-processing script. They use hand-written
// JSON files as input. Ideally the JSON files would contain comments
// explaining how they work, but JSON doesn't allow comments, so I've put
@ -147,37 +157,39 @@ function run_test() {
// of the tested values.
jsonFile = FileUtils.getFile("CurWorkD", ["script-max-frames.json"]);
test("script-max-frames-8",
["--ignore-reports", "--max-frames=8", jsonFile.path]);
["--max-frames=8", jsonFile.path]);
test("script-max-frames-3",
["--ignore-reports", "--max-frames=3", "--no-fix-stacks",
jsonFile.path]);
["--max-frames=3", "--no-fix-stacks", jsonFile.path]);
test("script-max-frames-1",
["--ignore-reports", "--max-frames=1", jsonFile.path]);
["--max-frames=1", jsonFile.path]);
// This file has three records that are shown in a different order for each
// of the different sort values. It also tests the handling of gzipped JSON
// files.
jsonFile = FileUtils.getFile("CurWorkD", ["script-sort-by.json.gz"]);
test("script-sort-by-usable",
["--ignore-reports", "--sort-by=usable", jsonFile.path]);
["--sort-by=usable", jsonFile.path]);
test("script-sort-by-req",
["--ignore-reports", "--sort-by=req", "--no-fix-stacks", jsonFile.path]);
["--sort-by=req", "--no-fix-stacks", jsonFile.path]);
test("script-sort-by-slop",
["--ignore-reports", "--sort-by=slop", jsonFile.path]);
["--sort-by=slop", jsonFile.path]);
// This file has several real stack traces taken from Firefox execution, each
// of which tests a different allocator function (or functions).
jsonFile = FileUtils.getFile("CurWorkD", ["script-ignore-alloc-fns.json"]);
test("script-ignore-alloc-fns",
["--ignore-reports", "--ignore-alloc-fns", jsonFile.path]);
["--ignore-alloc-fns", jsonFile.path]);
// This tests diffs. The first invocation has no options, the second has
// several.
jsonFile = FileUtils.getFile("CurWorkD", ["script-diff1.json"]);
jsonFile2 = FileUtils.getFile("CurWorkD", ["script-diff2.json"]);
test("script-diff-basic",
// This tests "live"-mode diffs.
jsonFile = FileUtils.getFile("CurWorkD", ["script-diff-live1.json"]);
jsonFile2 = FileUtils.getFile("CurWorkD", ["script-diff-live2.json"]);
test("script-diff-live",
[jsonFile.path, jsonFile2.path]);
// This tests "dark-matter"-mode diffs.
jsonFile = FileUtils.getFile("CurWorkD", ["script-diff-dark-matter1.json"]);
jsonFile2 = FileUtils.getFile("CurWorkD", ["script-diff-dark-matter2.json"]);
test("script-diff-dark-matter",
[jsonFile.path, jsonFile2.path]);
test("script-diff-options",
["--ignore-reports", jsonFile.path, jsonFile2.path]);
}

View File

@ -1,13 +1,11 @@
[DEFAULT]
support-files =
full-heap-empty-expected.txt
full-heap-unsampled1-expected.txt
full-heap-unsampled2-expected.txt
full-heap-sampled-expected.txt
full-reports-empty-expected.txt
full-reports-unsampled1-expected.txt
full-reports-unsampled2-expected.txt
full-reports-sampled-expected.txt
full-empty-live-expected.txt
full-empty-dark-matter-expected.txt
full-unsampled1-live-expected.txt
full-unsampled1-dark-matter-expected.txt
full-unsampled2-dark-matter-expected.txt
full-sampled-live-expected.txt
script-max-frames.json
script-max-frames-8-expected.txt
script-max-frames-3-expected.txt
@ -18,10 +16,12 @@ support-files =
script-sort-by-slop-expected.txt
script-ignore-alloc-fns.json
script-ignore-alloc-fns-expected.txt
script-diff1.json
script-diff2.json
script-diff-basic-expected.txt
script-diff-options-expected.txt
script-diff-live1.json
script-diff-live2.json
script-diff-live-expected.txt
script-diff-dark-matter1.json
script-diff-dark-matter2.json
script-diff-dark-matter-expected.txt
# Bug 1077230 explains why this test is disabled on Mac 10.6.
# Bug 1076446 comment 20 explains why this test is only enabled on Windows 5.1

View File

@ -893,6 +893,8 @@ 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',
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.')
@CommandArgument('--max-frames', default=None, type=str, group='DMD',
@ -900,7 +902,7 @@ class RunProgram(MachCommandBase):
@CommandArgument('--show-dump-stats', action='store_true', group='DMD',
help='Show stats when doing dumps.')
def run(self, params, remote, background, noprofile, debug, debugger,
debugparams, slowscript, dmd, sample_below, max_frames,
debugparams, slowscript, dmd, mode, sample_below, max_frames,
show_dump_stats):
try:
@ -967,6 +969,8 @@ class RunProgram(MachCommandBase):
if dmd:
dmd_params = []
if mode:
dmd_params.append('--mode=' + mode)
if sample_below:
dmd_params.append('--sample-below=' + sample_below)
if max_frames: