author | Nicholas Nethercote <nnethercote@mozilla.com> |
Mon, 08 Dec 2014 14:45:14 -0800 | |
changeset 218944 | c5229ba7f507cbde260d189782eda1120edb4353 |
parent 218943 | 3b926e37e776c0d015cebf461c4319193ef4cf89 |
child 218945 | 9fae0441be6665f1bc2fcca42adca87dfd990616 |
push id | 52688 |
push user | nnethercote@mozilla.com |
push date | Wed, 10 Dec 2014 01:26:49 +0000 |
treeherder | mozilla-inbound@9fae0441be66 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mccr8 |
bugs | 1094552 |
milestone | 37.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -300,38 +300,74 @@ class Options const T mDefault; const T mMax; T mActual; NumOption(T aDefault, T aMax) : mDefault(aDefault), mMax(aMax), mActual(aDefault) {} }; + // 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; void BadArg(const char* aArg); static const char* ValueIfMatch(const char* aArg, const char* aOptionName); static bool GetLong(const char* aArg, const char* aOptionName, long aMin, long aMax, long* aValue); static bool GetBool(const char* aArg, const char* aOptionName, bool* aValue); 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; //--------------------------------------------------------------------------- // The global lock //--------------------------------------------------------------------------- @@ -396,18 +432,21 @@ public: MOZ_ASSERT(mIsLocked); mIsLocked = false; MutexBase::Unlock(); } 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 { DISALLOW_COPY_AND_ASSIGN(AutoLockState); public: AutoLockState() { gStateLock->Lock(); } @@ -775,37 +814,42 @@ public: mUint |= (tag & kTagMask); } T Ptr() const { return reinterpret_cast<T>(mUint & kPtrMask); } 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; const size_t mReqSize; // size requested // 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; // This array has two elements because we record at most two reports of a // 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 // Analyze(). Only relevant if |mReportStackTrace| is non-nullptr. // // |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: LiveBlock(const void* aPtr, size_t aReqSize, const StackTrace* aAllocStackTrace, bool aIsSampled) : mPtr(aPtr), mReqSize(aReqSize), mAllocStackTrace_mIsSampled(aAllocStackTrace, aIsSampled), @@ -836,71 +880,80 @@ public: const StackTrace* AllocStackTrace() const { return mAllocStackTrace_mIsSampled.Ptr(); } 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; } if (ReportStackTrace1()) { return 1; } return 0; } // 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) { mReportStackTrace_mReportedOnAlloc[numReports].Set(StackTrace::Get(aT), aReportedOnAlloc); } } void UnreportIfNotReportedOnAlloc() const { + MOZ_ASSERT(gOptions->IsDarkMatterMode()); if (!ReportedOnAlloc1() && !ReportedOnAlloc2()) { mReportStackTrace_mReportedOnAlloc[0].Set(nullptr, 0); mReportStackTrace_mReportedOnAlloc[1].Set(nullptr, 0); } else if (!ReportedOnAlloc1() && ReportedOnAlloc2()) { // Shift the 2nd report down to the 1st one. mReportStackTrace_mReportedOnAlloc[0] = mReportStackTrace_mReportedOnAlloc[1]; @@ -1231,20 +1284,21 @@ Options::GetBool(const char* aArg, const // - Why that size? Because it's *much* faster but only moderately less precise // than a size of 1. // - Why prime? Because it makes our sampling more random. If we used a size // of 4096, for example, then our alloc counter would only take on even // values, because jemalloc always rounds up requests sizes. In contrast, a // 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) { bool isEnd = false; while (!isEnd) { // Consume leading whitespace. while (isspace(*e)) { e++; @@ -1260,17 +1314,23 @@ Options::Options(const char* aDMDEnvVar) } char replacedChar = *e; isEnd = replacedChar == '\0'; *e = '\0'; // 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)) { mMaxFrames.mActual = myLong; } else if (GetBool(arg, "--show-dump-stats", &myBool)) { mShowDumpStats = myBool; @@ -1293,16 +1353,18 @@ Options::BadArg(const char* aArg) { StatusMsg("\n"); StatusMsg("Bad entry in the $DMD environment variable: '%s'.\n", aArg); StatusMsg("\n"); StatusMsg("$DMD must be a whitespace-separated list of |--option=val|\n"); 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)); StatusMsg(" (prime numbers are recommended)\n"); StatusMsg(" --max-frames=<1..%d> Max. depth of stack traces [%d]\n", int(mMaxFrames.mMax), int(mMaxFrames.mDefault)); StatusMsg(" --show-dump-stats=<yes|no> Show stats about dumps? [no]\n"); @@ -1369,23 +1431,23 @@ Init(const malloc_table_t* aMallocTable) gLiveBlockTable = InfallibleAllocPolicy::new_<LiveBlockTable>(); gLiveBlockTable->init(8192); } gIsDMDInitialized = true; } //--------------------------------------------------------------------------- -// DMD reporting and unreporting +// Block reporting and unreporting //--------------------------------------------------------------------------- static void ReportHelper(const void* aPtr, bool aReportedOnAlloc) { - if (!aPtr) { + if (!gOptions->IsDarkMatterMode() || !aPtr) { return; } Thread* t = Thread::Fetch(); AutoBlockIntercepts block(t); AutoLockState lock; @@ -1411,22 +1473,19 @@ DMDFuncs::ReportOnAlloc(const void* aPtr ReportHelper(aPtr, /* onAlloc */ true); } //--------------------------------------------------------------------------- // DMD output //--------------------------------------------------------------------------- // 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 // primarily to aid DMD track heap blocks... but DMD deliberately doesn't track // heap blocks it allocated for itself! // // SizeOfInternal should be called while you're holding the state lock and // while intercepts are blocked; SizeOf acquires the lock and blocks @@ -1467,33 +1526,37 @@ DMDFuncs::SizeOf(Sizes* aSizes) AutoBlockIntercepts block(Thread::Fetch()); AutoLockState lock; SizeOfInternal(aSizes); } void DMDFuncs::ClearReports() { + if (!gOptions->IsDarkMatterMode()) { + 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 // their reported marking. for (auto r = gLiveBlockTable->all(); !r.empty(); r.popFront()) { r.front().UnreportIfNotReportedOnAlloc(); } } 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; PointerIdMap::AddPtr p = mIdMap.lookupForAdd(aPtr); if (!p) { id = mNextId++; (void)mIdMap.add(p, aPtr, id); } else { @@ -1571,16 +1634,26 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWr JSONWriter writer(Move(aWriter)); writer.Start(); { writer.IntProperty("version", kOutputVersionNumber); 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(); StatusMsg(" Constructing the heap block list...\n"); ToIdStringConverter isc; @@ -1594,17 +1667,18 @@ AnalyzeImpl(UniquePtr<JSONWriteFunc> aWr { if (!b.IsSampled()) { writer.IntProperty("req", b.ReqSize()); if (b.SlopSize() > 0) { writer.IntProperty("slop", b.SlopSize()); } } 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()) { writer.StringElement(isc.ToIdString(b.ReportStackTrace1())); } if (b.ReportStackTrace2()) { writer.StringElement(isc.ToIdString(b.ReportStackTrace2())); } @@ -1730,22 +1804,23 @@ DMDFuncs::Analyze(UniquePtr<JSONWriteFun ClearReports(); } //--------------------------------------------------------------------------- // Testing //--------------------------------------------------------------------------- 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; } } // namespace dmd } // namespace mozilla
--- a/memory/replace/dmd/DMD.h +++ b/memory/replace/dmd/DMD.h @@ -46,19 +46,17 @@ struct DMDFuncs virtual void ClearReports(); virtual void Analyze(UniquePtr<JSONWriteFunc>); virtual void SizeOf(Sizes*); 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 // did, the following would happen. // - The code footprint of each call to Get() larger as GetDMDFuncs ends // up inlined. // - When no replace-malloc library is loaded, the number of instructions // executed is equivalent, but don't necessarily fit in the same cache @@ -141,23 +139,31 @@ ClearReports() // The following sample output contains comments that explain the format and // design choices. The output files can be quite large, so a number of // decisions were made to minimize size, such as using short property names and // omitting properties whenever possible. // // { // // 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 // }, // // // Details of all analyzed heap blocks. A mandatory array. // "blockList": [ // // An example of a non-sampled heap block. // { @@ -177,18 +183,19 @@ ClearReports() // "alloc": "A" // }, // // // An example of a sampled heap block. // { // "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"] // } // ], // // // The stack traces referenced by elements of the "blockList" array. This // // could be an array, but making it an object makes it easier to see // // which stacks correspond to which references in the "blockList" array. // "traceTable": { @@ -216,21 +223,23 @@ ClearReports() // // that output stack traces in a conventional non-shared format. // "D": "#00: foo (Foo.cpp:123)", // "E": "#00: bar (Bar.cpp:234)", // "F": "#00: baz (Baz.cpp:345)", // "G": "#00: quux (Quux.cpp:456)", // "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) { DMDFuncs* funcs = DMDFuncs::Get(); if (funcs) { funcs->Analyze(Move(aWriteFunc)); } @@ -262,33 +271,25 @@ StatusMsg(const char* aFmt, ...) // Indicates whether or not DMD is running. inline bool 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 } // namespace dmd } // namespace mozilla #endif /* DMD_h___ */
--- a/memory/replace/dmd/dmd.py +++ b/memory/replace/dmd/dmd.py @@ -15,17 +15,17 @@ import json import os import platform import re import shutil 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 # we hit a non-matching frame, any subsequent frames won't be removed even if # they do match.) allocatorFns = [ 'replace_malloc', 'replace_calloc', @@ -179,20 +179,16 @@ variable is used to find breakpad symbol p = argparse.ArgumentParser(description=description) p.add_argument('-o', '--output', type=argparse.FileType('w'), help='output file; stdout if unspecified') 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') p.add_argument('-a', '--ignore-alloc-fns', action='store_true', help='ignore allocation functions at the start of traces') p.add_argument('--no-fix-stacks', action='store_true', @@ -273,21 +269,25 @@ def getDigestFromFile(args, inputFile): j = json.load(f) if j['version'] != outputVersion: raise Exception("'version' property isn't '{:d}'".format(outputVersion)) # 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. if args.ignore_alloc_fns: # Build a regexp that matches every function in allocatorFns. escapedAllocatorFns = map(re.escape, allocatorFns) fn_re = re.compile('|'.join(escapedAllocatorFns)) @@ -331,19 +331,19 @@ def getDigestFromFile(args, inputFile): desc = [] for n, frameKey in enumerate(traceTable[traceKey], start=1): desc.append(fmt.format(n, frameTable[frameKey][3:])) return desc # 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) heapUsableSize = 0 heapBlocks = 0 for block in blockList: @@ -364,20 +364,20 @@ def getDigestFromFile(args, inputFile): # and we trim the final frame of each they should be considered # equivalent because the untrimmed frame descriptions (D1 and D2) # match. def makeRecordKeyPart(traceKey): return str(map(lambda frameKey: frameTable[frameKey], 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'] for reportedAtTraceKey in reportedAtTraceKeys: recordKey += makeRecordKeyPart(reportedAtTraceKey) if len(reportedAtTraceKeys) == 1: records = onceReportedRecords else: @@ -409,34 +409,35 @@ def getDigestFromFile(args, inputFile): record.slopSize += slopSize record.usableSize += usableSize record.isSampled = record.isSampled or isSampled if record.allocatedAtDesc == None: record.allocatedAtDesc = \ 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) record.usableSizes[(usableSize, isSampled)] += 1 # 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 return digest def diffRecords(args, records1, records2): records3 = {} @@ -459,44 +460,49 @@ def diffRecords(args, records1, records2 for k in records2: # This record is present only in records2. records3[k] = records2[k] return records3 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'], d2['onceReportedRecords']) d3['twiceReportedRecords'] = diffRecords(args, d1['twiceReportedRecords'], d2['twiceReportedRecords']) return d3 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'] separator = '#' + '-' * 65 + '\n' def number(n, isSampled): '''Format a number, with comma as a separator and a '~' prefix if it's @@ -578,29 +584,29 @@ def printDigest(args, digest): if count > 1: out(' x {:,d}'.format(count), end='') isFirst = False out() 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, perc(kindCumulativeUsableSize, kindUsableSize))) 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)) printStack(reportedAtDesc) out(' }') out('}\n') return (kindUsableSize, kindBlocks) @@ -620,37 +626,37 @@ def printDigest(args, digest): # Print invocation(s). if type(dmdEnvVar) is not tuple: printInvocation('', dmdEnvVar, sampleBelowSize) else: printInvocation(' 1', dmdEnvVar[0], sampleBelowSize[0]) 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) unreportedUsableSize, unreportedBlocks = \ printRecords('unreported', unreportedRecords, heapUsableSize) onceReportedUsableSize, onceReportedBlocks = \ printRecords('once-reported', onceReportedRecords, heapUsableSize) # 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:', number(heapUsableSize, heapIsSampled), 100, number(heapBlocks, heapIsSampled), 100)) out(fmt.
--- a/memory/replace/dmd/test/SmokeDMD.cpp +++ b/memory/replace/dmd/test/SmokeDMD.cpp @@ -84,250 +84,280 @@ void Foo(int aSeven) UseItOrLoseIt(a[2], aSeven); Report(a[3]); // reported // a[4], a[5] unreported } void +TestEmpty(const char* aTestName, const char* aMode) +{ + char filename[128]; + sprintf(filename, "full-%s-%s.json", aTestName, aMode); + auto f = MakeUnique<FpWriteFunc>(filename); + + char options[128]; + sprintf(options, "--mode=%s --sample-below=1", aMode); + ResetEverything(options); + + // Zero for everything. + Analyze(Move(f)); +} + +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 --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: 1 freed, 9 out of 10 unreported. + // Analyze 2: still present and unreported. + int i; + char* a = nullptr; + for (i = 0; i < aSeven + 3; i++) { + a = (char*) malloc(100); + UseItOrLoseIt(a, aSeven); + } + free(a); + + // Note: 8 bytes is the smallest requested size that gives consistent + // behaviour across all platforms with jemalloc. + // Analyze 1: reported. + // Analyze 2: thrice-reported. + char* a2 = (char*) malloc(8); + Report(a2); + + // 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. + char* b2 = (char*) malloc(1); + ReportOnAlloc(b2); + free(b2); + + // Analyze 1: reported 4 times. + // Analyze 2: freed, irrelevant. + char* c = (char*) calloc(10, 3); + Report(c); + for (int i = 0; i < aSeven - 4; i++) { + Report(c); + } + + // Analyze 1: ignored. + // Analyze 2: irrelevant. + Report((void*)(intptr_t)i); + + // jemalloc rounds this up to 8192. + // 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 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 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. + char* f1 = (char*) malloc(64); + free(f1); + + // Analyze 1: ignored. + // Analyze 2: irrelevant. + Report((void*)(intptr_t)0x0); + + // Analyze 1: mixture of reported and unreported. + // Analyze 2: all unreported. + Foo(aSeven); + + // Analyze 1: twice-reported. + // Analyze 2: twice-reported. + char* g1 = (char*) malloc(77); + ReportOnAlloc(g1); + ReportOnAlloc(g1); + + // 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(aSeven); + + // Analyze 1: twice-reported. + // Analyze 2: once-reported. + char* g2 = (char*) malloc(78); + Report(g2); + ReportOnAlloc(g2); + + // Analyze 1: twice-reported. + // Analyze 2: once-reported. + char* g3 = (char*) malloc(79); + ReportOnAlloc(g3); + Report(g3); + + // All the odd-ball ones. + // 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, aSeven); + + // XXX: posix_memalign doesn't work on B2G +//void* x; +//posix_memalign(&y, 128, 129); // rounds up to 256 +//UseItOrLoseIt(x, aSeven); + + // XXX: valloc doesn't work on Windows. +//void* y = valloc(1); // rounds up to 4096 +//UseItOrLoseIt(y, aSeven); + + // XXX: C11 only +//void* z = aligned_alloc(64, 256); +//UseItOrLoseIt(z, aSeven); + + if (aNum == 1) { + // Analyze 1. + Analyze(Move(f)); + } + + ClearReports(); + + //--------- + + Report(a2); + Report(a2); + free(c); + free(e); + Report(e2); + free(e3); +//free(w); +//free(x); +//free(y); +//free(z); + + 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); + + 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, aSeven); + + // This exceeds the sample size, and so is reported exactly. + s = (char*) malloc(160); + UseItOrLoseIt(s, aSeven); + + // These together constitute exactly one sample. + for (int i = 0; i < aSeven + 9; i++) { + s = (char*) malloc(8); + UseItOrLoseIt(s, aSeven); + } + + // These fall 8 bytes short of a full sample. + for (int i = 0; i < aSeven + 8; i++) { + s = (char*) malloc(8); + UseItOrLoseIt(s, aSeven); + } + + // This exceeds the sample size, and so is recorded exactly. + s = (char*) malloc(256); + UseItOrLoseIt(s, aSeven); + + // This gets more than to a full sample from the |i < aSeven + 8| loop above. + s = (char*) malloc(96); + UseItOrLoseIt(s, aSeven); + + // This gets to another full sample. + for (int i = 0; i < aSeven - 2; i++) { + s = (char*) malloc(8); + 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 <= aSeven + 1; i++) { + s = (char*) malloc(i * 16); + 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(Move(f)); +} + +void RunTests() { - // 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"); - // 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()); - // The first part of this test requires sampling to be disabled. - SetSampleBelowSize(1); - - // The file manipulations above may have done some heap allocations. - // Clear all knowledge of existing blocks to give us a clean slate. - ClearBlocks(); - - //--------- - - // Analyze 1. Zero for everything. - Analyze(Move(f1)); - - //--------- - - // Analyze 2: 1 freed, 9 out of 10 unreported. - // Analyze 3: still present and unreported. - int i; - char* a = nullptr; - for (i = 0; i < seven + 3; i++) { - a = (char*) malloc(100); - UseItOrLoseIt(a, seven); - } - 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. - char* a2 = (char*) malloc(8); - Report(a2); - - // Analyze 2: reported. - // Analyze 3: reportedness carries over, due to ReportOnAlloc. - char* b = (char*) malloc(10); - ReportOnAlloc(b); - - // ReportOnAlloc, then freed. - // 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. - char* c = (char*) calloc(10, 3); - Report(c); - for (int i = 0; i < seven - 4; i++) { - Report(c); - } + // Please keep this in sync with run_test in test_dmd.js. - // Analyze 2: ignored. - // Analyze 3: irrelevant. - Report((void*)(intptr_t)i); - - // jemalloc rounds this up to 8192. - // Analyze 2: reported. - // Analyze 3: 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. - 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. - char* e3 = (char*) realloc(nullptr, 1023); -//e3 = (char*) realloc(e3, 0); - MOZ_ASSERT(e3); - Report(e3); - - // Analyze 2: freed, irrelevant. - // Analyze 3: freed, irrelevant. - char* f = (char*) malloc(64); - free(f); - - // Analyze 2: ignored. - // Analyze 3: irrelevant. - Report((void*)(intptr_t)0x0); - - // Analyze 2: mixture of reported and unreported. - // Analyze 3: all unreported. - Foo(seven); - - // 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. - // 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); + TestEmpty("empty", "live"); + TestEmpty("empty", "dark-matter"); - // Analyze 2: twice-reported. - // Analyze 3: once-reported. - char* g2 = (char*) malloc(78); - Report(g2); - ReportOnAlloc(g2); - - // Analyze 2: twice-reported. - // Analyze 3: 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. - // XXX: no memalign on Mac -//void* w = memalign(64, 65); // rounds up to 128 -//UseItOrLoseIt(w, seven); - - // XXX: posix_memalign doesn't work on B2G -//void* x; -//posix_memalign(&y, 128, 129); // rounds up to 256 -//UseItOrLoseIt(x, seven); - - // XXX: valloc doesn't work on Windows. -//void* y = valloc(1); // rounds up to 4096 -//UseItOrLoseIt(y, seven); - - // XXX: C11 only -//void* z = aligned_alloc(64, 256); -//UseItOrLoseIt(z, seven); - - // Analyze 2. - Analyze(Move(f2)); - - //--------- - - Report(a2); - Report(a2); - free(c); - free(e); - Report(e2); - free(e3); -//free(w); -//free(x); -//free(y); -//free(z); - - // Analyze 3. - Analyze(Move(f3)); - - //--------- + TestUnsampled("unsampled", 1, "live", seven); + TestUnsampled("unsampled", 1, "dark-matter", seven); - // 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* 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); - - // This exceeds the sample size, and so is reported exactly. - s = (char*) malloc(160); - UseItOrLoseIt(s, seven); - - // These together constitute exactly one sample. - for (int i = 0; i < seven + 9; i++) { - s = (char*) malloc(8); - UseItOrLoseIt(s, seven); - } + TestUnsampled("unsampled", 2, "dark-matter", seven); - // These fall 8 bytes short of a full sample. - for (int i = 0; i < seven + 8; i++) { - s = (char*) malloc(8); - UseItOrLoseIt(s, seven); - } - - // This exceeds the sample size, and so is recorded exactly. - s = (char*) malloc(256); - UseItOrLoseIt(s, seven); - - // This gets more than to a full sample from the |i < seven + 8| loop above. - s = (char*) malloc(96); - UseItOrLoseIt(s, seven); - - // This gets to another full sample. - for (int i = 0; i < seven - 2; i++) { - s = (char*) malloc(8); - UseItOrLoseIt(s, seven); - } - - // 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++) { - s = (char*) malloc(i * 16); - UseItOrLoseIt(s, seven); - } - - // 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)); + TestSampled("sampled", "live", seven); } int main() { RunTests(); return 0; }
rename from memory/replace/dmd/test/full-reports-empty-expected.txt rename to memory/replace/dmd/test/full-empty-dark-matter-expected.txt --- a/memory/replace/dmd/test/full-reports-empty-expected.txt +++ b/memory/replace/dmd/test/full-empty-dark-matter-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- -# 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 } #----------------------------------------------------------------- # no twice-reported heap blocks #-----------------------------------------------------------------
rename from memory/replace/dmd/test/full-heap-empty-expected.txt rename to memory/replace/dmd/test/full-empty-live-expected.txt --- a/memory/replace/dmd/test/full-heap-empty-expected.txt +++ b/memory/replace/dmd/test/full-empty-live-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- -# 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 } #----------------------------------------------------------------- # no live heap blocks #-----------------------------------------------------------------
deleted file mode 100644 --- a/memory/replace/dmd/test/full-heap-unsampled2-expected.txt +++ /dev/null @@ -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 -} -
deleted file mode 100644 --- a/memory/replace/dmd/test/full-reports-sampled-expected.txt +++ /dev/null @@ -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%) -} -
rename from memory/replace/dmd/test/full-heap-sampled-expected.txt rename to memory/replace/dmd/test/full-sampled-live-expected.txt --- a/memory/replace/dmd/test/full-heap-sampled-expected.txt +++ b/memory/replace/dmd/test/full-sampled-live-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- -# 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 } #----------------------------------------------------------------- Live { ~4 blocks in heap block record 1 of 7 ~512 bytes (~512 requested / ~0 slop)
rename from memory/replace/dmd/test/full-reports-unsampled1-expected.txt rename to memory/replace/dmd/test/full-unsampled1-dark-matter-expected.txt --- a/memory/replace/dmd/test/full-reports-unsampled1-expected.txt +++ b/memory/replace/dmd/test/full-unsampled1-dark-matter-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- -# 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 } #----------------------------------------------------------------- Twice-reported { 1 block in heap block record 1 of 4 80 bytes (79 requested / 1 slop)
rename from memory/replace/dmd/test/full-heap-unsampled1-expected.txt rename to memory/replace/dmd/test/full-unsampled1-live-expected.txt --- a/memory/replace/dmd/test/full-heap-unsampled1-expected.txt +++ b/memory/replace/dmd/test/full-unsampled1-live-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- -# 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 } #----------------------------------------------------------------- Live { 1 block in heap block record 1 of 12 8,192 bytes (4,097 requested / 4,095 slop)
rename from memory/replace/dmd/test/full-reports-unsampled2-expected.txt rename to memory/replace/dmd/test/full-unsampled2-dark-matter-expected.txt --- a/memory/replace/dmd/test/full-reports-unsampled2-expected.txt +++ b/memory/replace/dmd/test/full-unsampled2-dark-matter-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- -# 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 } #----------------------------------------------------------------- Twice-reported { 1 block in heap block record 1 of 2 80 bytes (77 requested / 3 slop)
new file mode 100644 --- /dev/null +++ b/memory/replace/dmd/test/script-diff-dark-matter-expected.txt @@ -0,0 +1,127 @@ +#----------------------------------------------------------------- +# 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' + Sample-below size = 127 +} + +Invocation 2 { + $DMD = '--sample-below=63' + Sample-below size = 63 +} + +#----------------------------------------------------------------- + +Twice-reported { + ~-1 blocks in heap block record 1 of 1 + ~-1,088 bytes (~-1,064 requested / ~-24 slop) + Individual block sizes: -1,024; ~-127; ~63 + 15.46% of the heap (15.46% cumulative) + 100.00% of twice-reported (100.00% cumulative) + Allocated at { + #01: F (F.cpp:99) + } + Reported at { + #01: R1 (R1.cpp:99) + } + Reported again at { + #01: R2 (R2.cpp:99) + } +} + +#----------------------------------------------------------------- + +Unreported { + 4 blocks in heap block record 1 of 5 + 16,384 bytes (16,384 requested / 0 slop) + Individual block sizes: 4,096 x 4 + -232.76% of the heap (-232.76% cumulative) + 371.01% of unreported (371.01% cumulative) + Allocated at { + #01: E (E.cpp:99) + } +} + +Unreported { + ~7 blocks in heap block record 2 of 5 + ~-11,968 bytes (~-12,016 requested / ~48 slop) + Individual block sizes: -15,360; 2,048; 512 x 2; 128; ~-127; 64 x 4; ~63 + 170.02% of the heap (-62.74% cumulative) + -271.01% of unreported (100.00% cumulative) + Allocated at { + #01: F (F.cpp:99) + } +} + +Unreported { + 0 blocks in heap block record 3 of 5 + 0 bytes (-384 requested / 384 slop) + Individual block sizes: (no change) + -0.00% of the heap (-62.74% cumulative) + 0.00% of unreported (100.00% cumulative) + Allocated at { + #01: C (C.cpp:99) + } +} + +Unreported { + -2 blocks in heap block record 4 of 5 + 0 bytes (0 requested / 0 slop) + Individual block sizes: 8,192 x 2; -4,096 x 4 + -0.00% of the heap (-62.74% cumulative) + 0.00% of unreported (100.00% cumulative) + Allocated at { + #01: B (B.cpp:99) + } +} + +Unreported { + 0 blocks in heap block record 5 of 5 + 0 bytes (0 requested / 0 slop) + Individual block sizes: 20,480; -16,384; -8,192; 4,096 + -0.00% of the heap (-62.74% cumulative) + 0.00% of unreported (100.00% cumulative) + Allocated at { + #01: G (G.cpp:99) + } +} + +#----------------------------------------------------------------- + +Once-reported { + -3 blocks in heap block record 1 of 2 + -10,240 bytes (-10,192 requested / -48 slop) + Individual block sizes: -4,096 x 2; -2,048 + 145.48% of the heap (145.48% cumulative) + 98.77% of once-reported (98.77% cumulative) + Allocated at { + #01: D (D.cpp:99) + } + Reported at { + #01: R1 (R1.cpp:99) + } +} + +Once-reported { + ~-1 blocks in heap block record 2 of 2 + ~-127 bytes (~-151 requested / ~24 slop) + 1.80% of the heap (147.28% cumulative) + 1.23% of once-reported (100.00% cumulative) + Allocated at { + #01: F (F.cpp:99) + } + Reported at { + #01: R1 (R1.cpp:99) + } +} + +#----------------------------------------------------------------- + +Summary { + Total: ~-7,039 bytes (100.00%) in ~4 blocks (100.00%) + Unreported: ~4,416 bytes (-62.74%) in ~9 blocks (225.00%) + Once-reported: ~-10,367 bytes (147.28%) in ~-4 blocks (-100.00%) + Twice-reported: ~-1,088 bytes ( 15.46%) in ~-1 blocks (-25.00%) +} +
new file mode 100644 --- /dev/null +++ b/memory/replace/dmd/test/script-diff-dark-matter1.json @@ -0,0 +1,63 @@ +{ + "version": 2, + "invocation": { + "dmdEnvVar": "--sample-below=127", + "mode": "dark-matter", + "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", "reps": ["R1"]}, + {"req": 4096, "alloc": "D", "reps": ["R1"]}, + {"req": 2000, "slop": 48, "alloc": "D", "reps": ["R1"]}, + + {"req": 15360, "alloc": "F"}, + {"req": 512, "alloc": "F"}, + {"req": 512, "alloc": "F"}, + { "alloc": "F"}, + {"req": 1024, "alloc": "F", "reps": ["R1"]}, + { "alloc": "F", "reps": ["R1"]}, + {"req": 1000, "slop": 24, "alloc": "F", "reps": ["R1", "R2"]}, + { "alloc": "F", "reps": ["R1", "R2"]}, + + {"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)" + } +}
new file mode 100644 --- /dev/null +++ b/memory/replace/dmd/test/script-diff-dark-matter2.json @@ -0,0 +1,67 @@ +{ + "version": 2, + "invocation": { + "dmdEnvVar": "--sample-below=63", + "mode": "dark-matter", + "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", "reps": ["R1"]}, + {"req": 512, "alloc": "F"}, + {"req": 512, "alloc": "F"}, + {"req": 512, "alloc": "F"}, + {"req": 512, "alloc": "F"}, + {"req": 128, "alloc": "F"}, + { "alloc": "F", "reps": ["R1", "R2"]}, + {"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)" + } +}
rename from memory/replace/dmd/test/script-diff-basic-expected.txt rename to memory/replace/dmd/test/script-diff-live-expected.txt --- a/memory/replace/dmd/test/script-diff-basic-expected.txt +++ b/memory/replace/dmd/test/script-diff-live-expected.txt @@ -1,127 +1,81 @@ #----------------------------------------------------------------- -# 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-live-actual.txt script-diff-live1.json script-diff-live2.json Invocation 1 { $DMD = '--sample-below=127' Sample-below size = 127 } Invocation 2 { $DMD = '--sample-below=63' Sample-below size = 63 } #----------------------------------------------------------------- -Twice-reported { - ~-1 blocks in heap block record 1 of 1 - ~-1,088 bytes (~-1,064 requested / ~-24 slop) - Individual block sizes: -1,024; ~-127; ~63 - 15.46% of the heap (15.46% cumulative) - 100.00% of twice-reported (100.00% cumulative) - Allocated at { - #01: F (F.cpp:99) - } - Reported at { - #01: R1 (R1.cpp:99) - } - Reported again at { - #01: R2 (R2.cpp:99) - } -} - -#----------------------------------------------------------------- - -Unreported { - 4 blocks in heap block record 1 of 5 +Live { + 4 blocks in heap block record 1 of 6 16,384 bytes (16,384 requested / 0 slop) Individual block sizes: 4,096 x 4 -232.76% of the heap (-232.76% cumulative) - 371.01% of unreported (371.01% cumulative) Allocated at { #01: E (E.cpp:99) } } -Unreported { - ~7 blocks in heap block record 2 of 5 - ~-11,968 bytes (~-12,016 requested / ~48 slop) - Individual block sizes: -15,360; 2,048; 512 x 2; 128; ~-127; 64 x 4; ~63 - 170.02% of the heap (-62.74% cumulative) - -271.01% of unreported (100.00% cumulative) +Live { + ~5 blocks in heap block record 2 of 6 + ~-13,183 bytes (~-13,231 requested / ~48 slop) + Individual block sizes: -15,360; 2,048; -1,024; 512 x 2; 128; ~-127 x 3; 64 x 4; ~63 x 2 + 187.29% of the heap (-45.48% cumulative) Allocated at { #01: F (F.cpp:99) } } -Unreported { - 0 blocks in heap block record 3 of 5 +Live { + -3 blocks in heap block record 3 of 6 + -10,240 bytes (-10,192 requested / -48 slop) + Individual block sizes: -4,096 x 2; -2,048 + 145.48% of the heap (100.00% cumulative) + Allocated at { + #01: D (D.cpp:99) + } +} + +Live { + 0 blocks in heap block record 4 of 6 0 bytes (-384 requested / 384 slop) Individual block sizes: (no change) - -0.00% of the heap (-62.74% cumulative) - 0.00% of unreported (100.00% cumulative) + -0.00% of the heap (100.00% cumulative) Allocated at { #01: C (C.cpp:99) } } -Unreported { - -2 blocks in heap block record 4 of 5 - 0 bytes (0 requested / 0 slop) - Individual block sizes: 8,192 x 2; -4,096 x 4 - -0.00% of the heap (-62.74% cumulative) - 0.00% of unreported (100.00% cumulative) - Allocated at { - #01: B (B.cpp:99) - } -} - -Unreported { - 0 blocks in heap block record 5 of 5 +Live { + 0 blocks in heap block record 5 of 6 0 bytes (0 requested / 0 slop) Individual block sizes: 20,480; -16,384; -8,192; 4,096 - -0.00% of the heap (-62.74% cumulative) - 0.00% of unreported (100.00% cumulative) + -0.00% of the heap (100.00% cumulative) Allocated at { #01: G (G.cpp:99) } } -#----------------------------------------------------------------- - -Once-reported { - -3 blocks in heap block record 1 of 2 - -10,240 bytes (-10,192 requested / -48 slop) - Individual block sizes: -4,096 x 2; -2,048 - 145.48% of the heap (145.48% cumulative) - 98.77% of once-reported (98.77% cumulative) +Live { + -2 blocks in heap block record 6 of 6 + 0 bytes (0 requested / 0 slop) + Individual block sizes: 8,192 x 2; -4,096 x 4 + -0.00% of the heap (100.00% cumulative) Allocated at { - #01: D (D.cpp:99) - } - Reported at { - #01: R1 (R1.cpp:99) - } -} - -Once-reported { - ~-1 blocks in heap block record 2 of 2 - ~-127 bytes (~-151 requested / ~24 slop) - 1.80% of the heap (147.28% cumulative) - 1.23% of once-reported (100.00% cumulative) - Allocated at { - #01: F (F.cpp:99) - } - Reported at { - #01: R1 (R1.cpp:99) + #01: B (B.cpp:99) } } #----------------------------------------------------------------- Summary { - Total: ~-7,039 bytes (100.00%) in ~4 blocks (100.00%) - Unreported: ~4,416 bytes (-62.74%) in ~9 blocks (225.00%) - Once-reported: ~-10,367 bytes (147.28%) in ~-4 blocks (-100.00%) - Twice-reported: ~-1,088 bytes ( 15.46%) in ~-1 blocks (-25.00%) + Total: ~-7,039 bytes in ~4 blocks }
rename from memory/replace/dmd/test/script-diff1.json rename to memory/replace/dmd/test/script-diff-live1.json --- a/memory/replace/dmd/test/script-diff1.json +++ b/memory/replace/dmd/test/script-diff-live1.json @@ -1,12 +1,13 @@ { - "version": 1, + "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"}, @@ -15,28 +16,28 @@ {"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", "reps": ["R1"]}, - {"req": 4096, "alloc": "D", "reps": ["R1"]}, - {"req": 2000, "slop": 48, "alloc": "D", "reps": ["R1"]}, + {"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", "reps": ["R1"]}, - { "alloc": "F", "reps": ["R1"]}, - {"req": 1000, "slop": 24, "alloc": "F", "reps": ["R1", "R2"]}, - { "alloc": "F", "reps": ["R1", "R2"]}, + {"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"],
rename from memory/replace/dmd/test/script-diff2.json rename to memory/replace/dmd/test/script-diff-live2.json --- a/memory/replace/dmd/test/script-diff2.json +++ b/memory/replace/dmd/test/script-diff-live2.json @@ -1,12 +1,13 @@ { - "version": 1, + "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"}, @@ -19,23 +20,23 @@ {"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", "reps": ["R1"]}, + {"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", "reps": ["R1", "R2"]}, + { "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"},
deleted file mode 100644 --- a/memory/replace/dmd/test/script-diff-options-expected.txt +++ /dev/null @@ -1,81 +0,0 @@ -#----------------------------------------------------------------- -# dmd.py --filter-stacks-for-testing -o script-diff-options-actual.txt --ignore-reports script-diff1.json script-diff2.json - -Invocation 1 { - $DMD = '--sample-below=127' - Sample-below size = 127 -} - -Invocation 2 { - $DMD = '--sample-below=63' - Sample-below size = 63 -} - -#----------------------------------------------------------------- - -Live { - 4 blocks in heap block record 1 of 6 - 16,384 bytes (16,384 requested / 0 slop) - Individual block sizes: 4,096 x 4 - -232.76% of the heap (-232.76% cumulative) - Allocated at { - #01: E (E.cpp:99) - } -} - -Live { - ~5 blocks in heap block record 2 of 6 - ~-13,183 bytes (~-13,231 requested / ~48 slop) - Individual block sizes: -15,360; 2,048; -1,024; 512 x 2; 128; ~-127 x 3; 64 x 4; ~63 x 2 - 187.29% of the heap (-45.48% cumulative) - Allocated at { - #01: F (F.cpp:99) - } -} - -Live { - -3 blocks in heap block record 3 of 6 - -10,240 bytes (-10,192 requested / -48 slop) - Individual block sizes: -4,096 x 2; -2,048 - 145.48% of the heap (100.00% cumulative) - Allocated at { - #01: D (D.cpp:99) - } -} - -Live { - 0 blocks in heap block record 4 of 6 - 0 bytes (-384 requested / 384 slop) - Individual block sizes: (no change) - -0.00% of the heap (100.00% cumulative) - Allocated at { - #01: C (C.cpp:99) - } -} - -Live { - 0 blocks in heap block record 5 of 6 - 0 bytes (0 requested / 0 slop) - Individual block sizes: 20,480; -16,384; -8,192; 4,096 - -0.00% of the heap (100.00% cumulative) - Allocated at { - #01: G (G.cpp:99) - } -} - -Live { - -2 blocks in heap block record 6 of 6 - 0 bytes (0 requested / 0 slop) - Individual block sizes: 8,192 x 2; -4,096 x 4 - -0.00% of the heap (100.00% cumulative) - Allocated at { - #01: B (B.cpp:99) - } -} - -#----------------------------------------------------------------- - -Summary { - Total: ~-7,039 bytes in ~4 blocks -} -
--- a/memory/replace/dmd/test/script-ignore-alloc-fns-expected.txt +++ b/memory/replace/dmd/test/script-ignore-alloc-fns-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 2500 } #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-ignore-alloc-fns.json +++ b/memory/replace/dmd/test/script-ignore-alloc-fns.json @@ -1,12 +1,13 @@ { - "version": 1, + "version": 2, "invocation": { "dmdEnvVar": "1", + "mode": "live", "sampleBelowSize": 2500 }, "blockList": [ {"req": 1048576, "alloc": "A"}, {"req": 65536, "alloc": "B"}, {"req": 8000, "slop": 192, "alloc": "C"}, { "alloc": "D"} ],
--- a/memory/replace/dmd/test/script-max-frames-1-expected.txt +++ b/memory/replace/dmd/test/script-max-frames-1-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 1 } #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-max-frames-3-expected.txt +++ b/memory/replace/dmd/test/script-max-frames-3-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 1 } #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-max-frames-8-expected.txt +++ b/memory/replace/dmd/test/script-max-frames-8-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 1 } #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-max-frames.json +++ b/memory/replace/dmd/test/script-max-frames.json @@ -1,12 +1,13 @@ { - "version": 1, + "version": 2, "invocation": { "dmdEnvVar": "1", + "mode": "live", "sampleBelowSize": 1 }, "blockList": [ {"req": 4096, "alloc": "A"}, {"req": 128, "alloc": "B"}, {"req": 100, "slop":12, "alloc": "C"}, {"req": 80, "alloc": "D"} ],
--- a/memory/replace/dmd/test/script-sort-by-req-expected.txt +++ b/memory/replace/dmd/test/script-sort-by-req-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 1 } #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-sort-by-slop-expected.txt +++ b/memory/replace/dmd/test/script-sort-by-slop-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 1 } #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-sort-by-usable-expected.txt +++ b/memory/replace/dmd/test/script-sort-by-usable-expected.txt @@ -1,10 +1,10 @@ #----------------------------------------------------------------- -# 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' Sample-below size = 1 } #-----------------------------------------------------------------
index f8b602a7359a80984fffd76099696733288788a8..fb767c74adca6ee27c176fea47d23aff1e65ef89 GIT binary patch literal 295 zc$@(z0oeW@iwFoe3UpKg19M|?X>fEcb8m8VEn;~tYIARH0L_xkPJ}QNh4+1mo9hM> zV#+ufg<adYbEk1*SahH!jfG+%m`LW`ZDB;<0d%_J{O~nR&OiWr<yviP%wQLLsq5Yr zvYVWN0AN{_4|V?{oi{MSICzyURggyaYI0gxHAZn|?B|pI3SmOv6o^M-i}y#}cHVpS z)d8LQ@ZWoy9%4Xgd>&W~L;Y{!`xyM$#EblvQtE$ijcvk9HZZ=1T}U}OjE`EKO?lDY z(S7=h_Udkl7HjlBmI>rxM4gj`dX`6{erCzSoRBfh$Nlk9a(<R#ltN&BUg4XQ)dmm< tctvRj3?g<@G)<PK*VCLipF<82Uxz}>p@2xtp}&fp$Tzm8!XZ@y0012MjGh1h
--- a/memory/replace/dmd/test/test_dmd.js +++ b/memory/replace/dmd/test/test_dmd.js @@ -123,61 +123,73 @@ function run_test() { // in-place (to fix stacks) when it runs dmd.py, and that's not safe to do // asynchronously. gEnv.set("DMD", "1"); gEnv.set(gEnv.get("DMD_PRELOAD_VAR"), gEnv.get("DMD_PRELOAD_VALUE")); 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 // explanations here. // This just tests that stack traces of various lengths are truncated // appropriately. The number of records in the output is different for each // 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]); - test("script-diff-options", - ["--ignore-reports", 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]); }
--- a/memory/replace/dmd/test/xpcshell.ini +++ b/memory/replace/dmd/test/xpcshell.ini @@ -1,31 +1,31 @@ [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 script-max-frames-1-expected.txt script-sort-by.json.gz script-sort-by-usable-expected.txt script-sort-by-req-expected.txt 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 # (WinXP) and 6.1 (Win7), but not 6.2 (Win8). [test_dmd.js] dmd = true run-if = os == 'linux' || os == 'mac' && os_version != '10.6' || os == 'win' && (os_version == '5.1' || os_version == '6.1')
--- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -888,24 +888,26 @@ class RunProgram(MachCommandBase): # "continue" to (safely) resume execution. There are ways to implement # automatic resuming; see the bug. @CommandArgument('--slowscript', action='store_true', group='debugging', help='Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; when not set, recoverable but misleading SIGSEGV instances may occur in Ion/Odin JIT code.') @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', help='The maximum depth of stack traces. The default and maximum is 24.') @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: binpath = self.get_binary_path('app') except Exception as e: print("It looks like your program isn't built.", "You can run |mach build| to build it.") print(e) @@ -962,16 +964,18 @@ class RunProgram(MachCommandBase): extra_env['MOZ_CRASHREPORTER_DISABLE'] = '1' # Prepend the debugger args. args = [self.debuggerInfo.path] + self.debuggerInfo.args + args if dmd: dmd_params = [] + if mode: + dmd_params.append('--mode=' + mode) if sample_below: dmd_params.append('--sample-below=' + sample_below) if max_frames: dmd_params.append('--max-frames=' + max_frames) if show_dump_stats: dmd_params.append('--show-dump-stats=yes') if dmd_params: