--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -1397,28 +1397,26 @@ Init(const malloc_table_t* aMallocTable)
gMallocTable = aMallocTable;
// DMD is controlled by the |DMD| environment variable.
// - If it's unset or empty or "0", DMD doesn't run.
// - Otherwise, the contents dictate DMD's behaviour.
char* e = getenv("DMD");
- StatusMsg("$DMD = '%s'\n", e);
if (!e || strcmp(e, "") == 0 || strcmp(e, "0") == 0) {
- StatusMsg("DMD is not enabled\n");
return;
}
+ StatusMsg("$DMD = '%s'\n", e);
+
// Parse $DMD env var.
gOptions = InfallibleAllocPolicy::new_<Options>(e);
- StatusMsg("DMD is enabled\n");
-
#ifdef XP_MACOSX
// On Mac OS X we need to call StackWalkInitCriticalAddress() very early
// (prior to the creation of any mutexes, apparently) otherwise we can get
// hangs when getting stack traces (bug 821577). But
// StackWalkInitCriticalAddress() isn't exported from xpcom/, so instead we
// just call NS_StackWalk, because that calls StackWalkInitCriticalAddress().
// See the comment above StackWalkInitCriticalAddress() for more details.
(void)NS_StackWalk(NopStackWalkCallback, /* skipFrames */ 0,
@@ -1440,29 +1438,37 @@ Init(const malloc_table_t* aMallocTable)
gBlockTable = InfallibleAllocPolicy::new_<BlockTable>();
gBlockTable->init(8192);
}
if (gOptions->IsTestMode()) {
// Do all necessary allocations before setting gIsDMDRunning so those
// allocations don't show up in our results. Once gIsDMDRunning is set we
// are intercepting malloc et al. in earnest.
+ //
+ // These files are written to $CWD. It would probably be better to write
+ // them to "TmpD" using the directory service, but that would require
+ // linking DMD with XPCOM.
auto f1 = MakeUnique<FpWriteFunc>(OpenOutputFile("full1.json"));
auto f2 = MakeUnique<FpWriteFunc>(OpenOutputFile("full2.json"));
auto f3 = MakeUnique<FpWriteFunc>(OpenOutputFile("full3.json"));
auto f4 = MakeUnique<FpWriteFunc>(OpenOutputFile("full4.json"));
gIsDMDRunning = true;
StatusMsg("running test mode...\n");
RunTestMode(Move(f1), Move(f2), Move(f3), Move(f4));
- StatusMsg("finished test mode\n");
- exit(0);
+ StatusMsg("finished test mode; DMD is now disabled again\n");
+
+ // Continue running so that the xpcshell test can complete, but DMD no
+ // longer needs to be running.
+ gIsDMDRunning = false;
+
+ } else {
+ gIsDMDRunning = true;
}
-
- gIsDMDRunning = true;
}
//---------------------------------------------------------------------------
// DMD reporting and unreporting
//---------------------------------------------------------------------------
static void
ReportHelper(const void* aPtr, bool aReportedOnAlloc)
@@ -1830,71 +1836,88 @@ AnalyzeReports(JSONWriter& aWriter)
}
//---------------------------------------------------------------------------
// Testing
//---------------------------------------------------------------------------
// This function checks that heap blocks that have the same stack trace but
// different (or no) reporters get aggregated separately.
-void foo()
+void Foo(int aSeven)
{
- char* a[6];
- for (int i = 0; i < 6; i++) {
- a[i] = (char*) malloc(128 - 16*i);
- }
+ char* a[6];
+ for (int i = 0; i < aSeven - 1; i++) {
+ a[i] = (char*) malloc(128 - 16*i);
+ }
- for (int i = 0; i <= 1; i++)
- Report(a[i]); // reported
- Report(a[2]); // reported
- Report(a[3]); // reported
- // a[4], a[5] unreported
+ for (int i = 0; i < aSeven - 5; i++) {
+ Report(a[i]); // reported
+ }
+ Report(a[2]); // reported
+ Report(a[3]); // reported
+ // a[4], a[5] unreported
}
// This stops otherwise-unused variables from being optimized away.
static void
-UseItOrLoseIt(void* a)
+UseItOrLoseIt(void* aPtr, int aSeven)
{
char buf[64];
- sprintf(buf, "%p\n", a);
- fwrite(buf, 1, strlen(buf) + 1, stderr);
+ int n = sprintf(buf, "%p\n", aPtr);
+ if (n == 20 + aSeven) {
+ fprintf(stderr, "well, that is surprising");
+ }
}
-// The output from this should be tested with check_test_output.py. It's been
-// tested on Linux64, and probably will give different results on other
-// platforms.
+// The output from this function feeds into DMD's xpcshell test.
static void
RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4)
{
+ // 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 want a constant value that the compiler can't determine
+ // statically, and we use that in various ways to prevent the above
+ // optimizations from happening.
+ //
+ // This code always sets |seven| to the value 7. It works because we know
+ // that "--mode=test" must be within the DMD environment variable if we reach
+ // here, but the compiler almost certainly does not.
+ //
+ char* env = getenv("DMD");
+ char* p1 = strstr(env, "--mode=t");
+ char* p2 = strstr(p1, "test");
+ int seven = p2 - p1;
+
// The first part of this test requires sampling to be disabled.
gOptions->SetSampleBelowSize(1);
//---------
// AnalyzeReports 1. Zero for everything.
JSONWriter writer1(Move(aF1));
AnalyzeReports(writer1);
//---------
// AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
// AnalyzeReports 3: still present and unreported.
int i;
- char* a;
- for (i = 0; i < 10; i++) {
+ char* a = nullptr;
+ for (i = 0; i < seven + 3; i++) {
a = (char*) malloc(100);
- UseItOrLoseIt(a);
+ UseItOrLoseIt(a, seven);
}
free(a);
- // Min-sized block.
+ // Note: 8 bytes is the smallest requested size that gives consistent
+ // behaviour across all platforms with jemalloc.
// AnalyzeReports 2: reported.
// AnalyzeReports 3: thrice-reported.
- char* a2 = (char*) malloc(0);
+ char* a2 = (char*) malloc(8);
Report(a2);
// Operator new[].
// AnalyzeReports 2: reported.
// AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
char* b = new char[10];
ReportOnAlloc(b);
@@ -1904,17 +1927,17 @@ RunTestMode(UniquePtr<FpWriteFunc> aF1,
char* b2 = new char;
ReportOnAlloc(b2);
free(b2);
// AnalyzeReports 2: reported 4 times.
// AnalyzeReports 3: freed, irrelevant.
char* c = (char*) calloc(10, 3);
Report(c);
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < seven - 4; i++) {
Report(c);
}
// AnalyzeReports 2: ignored.
// AnalyzeReports 3: irrelevant.
Report((void*)(intptr_t)i);
// jemalloc rounds this up to 8192.
@@ -1946,18 +1969,18 @@ RunTestMode(UniquePtr<FpWriteFunc> aF1,
free(f);
// AnalyzeReports 2: ignored.
// AnalyzeReports 3: irrelevant.
Report((void*)(intptr_t)0x0);
// AnalyzeReports 2: mixture of reported and unreported.
// AnalyzeReports 3: all unreported.
- foo();
- foo();
+ Foo(seven);
+ Foo(seven);
// AnalyzeReports 2: twice-reported.
// AnalyzeReports 3: twice-reported.
char* g1 = (char*) malloc(77);
ReportOnAlloc(g1);
ReportOnAlloc(g1);
// AnalyzeReports 2: twice-reported.
@@ -1972,24 +1995,24 @@ RunTestMode(UniquePtr<FpWriteFunc> aF1,
ReportOnAlloc(g3);
Report(g3);
// All the odd-ball ones.
// AnalyzeReports 2: all unreported.
// AnalyzeReports 3: all freed, irrelevant.
// XXX: no memalign on Mac
//void* x = memalign(64, 65); // rounds up to 128
-//UseItOrLoseIt(x);
+//UseItOrLoseIt(x, seven);
// XXX: posix_memalign doesn't work on B2G
//void* y;
//posix_memalign(&y, 128, 129); // rounds up to 256
-//UseItOrLoseIt(y);
+//UseItOrLoseIt(y, seven);
// XXX: valloc doesn't work on Windows.
//void* z = valloc(1); // rounds up to 4096
-//UseItOrLoseIt(z);
+//UseItOrLoseIt(z, seven);
//aligned_alloc(64, 256); // XXX: C11 only
// AnalyzeReports 2.
JSONWriter writer2(Move(aF2));
AnalyzeReports(writer2);
//---------
@@ -2014,59 +2037,59 @@ RunTestMode(UniquePtr<FpWriteFunc> aF1,
gOptions->SetSampleBelowSize(128);
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);
+ UseItOrLoseIt(s, seven);
// This exceeds the sample size, and so is reported exactly.
s = (char*) malloc(144);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
// These together constitute exactly one sample.
- for (int i = 0; i < 16; i++) {
+ for (int i = 0; i < seven + 9; i++) {
s = (char*) malloc(8);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
}
MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
// These fall 8 bytes short of a full sample.
- for (int i = 0; i < 15; i++) {
+ for (int i = 0; i < seven + 8; i++) {
s = (char*) malloc(8);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
}
MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
// This exceeds the sample size, and so is recorded exactly.
s = (char*) malloc(256);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
// This gets more than to a full sample from the |i < 15| loop above.
s = (char*) malloc(96);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
MOZ_ASSERT(gSmallBlockActualSizeCounter == 88);
// This gets to another full sample.
- for (int i = 0; i < 5; i++) {
+ for (int i = 0; i < seven - 2; i++) {
s = (char*) malloc(8);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
}
MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
// 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 <= 8; i++) {
+ for (int i = 1; i <= seven + 1; i++) {
s = (char*) malloc(i * 16);
- UseItOrLoseIt(s);
+ UseItOrLoseIt(s, seven);
}
MOZ_ASSERT(gSmallBlockActualSizeCounter == 64);
// 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.
// AnalyzeReports 4.
JSONWriter writer4(Move(aF4));
deleted file mode 100755
--- a/memory/replace/dmd/check_test_output.py
+++ /dev/null
@@ -1,127 +0,0 @@
-#! /usr/bin/python
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-"""This script takes the file produced by DMD's test mode and checks its
-correctness.
-
-It produces the following output files: $TMP/full-{fixed,filtered,diff}.dmd.
-
-It runs the appropriate fix* script to get nice stack traces. It also
-filters out platform-specific details from the test output file.
-
-Note: you must run this from the same directory that you invoked DMD's test
-mode, otherwise the fix* script will not work properly, because some of the
-paths in the test output are relative.
-
-"""
-
-from __future__ import print_function
-
-import os
-import platform
-import re
-import subprocess
-import sys
-import tempfile
-
-def test(src_dir, kind, options, i):
- # Filenames
- tmp_dir = tempfile.gettempdir()
- in_name = os.path.join(src_dir, "full{:d}.json".format(i))
- fixed_name = os.path.join(tmp_dir, "full-{:}-fixed{:d}.json".format(kind, i))
- converted_name = os.path.join(tmp_dir, "full-{:}-converted{:d}.txt".format(kind, i))
- filtered_name = os.path.join(tmp_dir, "full-{:}-filtered{:d}.txt".format(kind, i))
- diff_name = os.path.join(tmp_dir, "full-{:}-diff{:d}.txt".format(kind, i))
- expected_name = os.path.join(src_dir, "memory", "replace", "dmd", "test", "full-{:}-expected{:d}.txt".format(kind, i))
-
- # Fix stack traces
-
- sys_name = platform.system()
- fix = os.path.join(src_dir, "tools", "rb")
- if sys_name == "Linux":
- fix = os.path.join(fix, "fix_linux_stack.py")
- elif sys_name == "Darwin":
- fix = os.path.join(fix, "fix_macosx_stack.py")
- else:
- print("unhandled platform: " + sys_name, file=sys.stderr)
- sys.exit(1)
-
- subprocess.call(fix, stdin=open(in_name, "r"),
- stdout=open(fixed_name, "w"))
-
- # Convert from JSON
-
- convert = [os.path.join(src_dir, "memory", "replace", "dmd", "dmd.py")] + \
- options + ['--no-fix-stacks', fixed_name]
- subprocess.call(convert, stdout=open(converted_name, "w"))
-
- # Filter output
-
- # In heap block records we filter out most stack frames. The only thing
- # we leave behind is a "DMD.cpp" entry if we see one or more frames that
- # have DMD.cpp in them. There is simply too much variation to do anything
- # better than that.
-
- with open(converted_name, "r") as fin, \
- open(filtered_name, "w") as fout:
-
- test_frame_re = re.compile(r".*(DMD.cpp)")
-
- for line in fin:
- if re.match(r" (Allocated at {|Reported( again)? at {)", line):
- # It's a heap block record.
- print(line, end='', file=fout)
-
- # Filter the stack trace -- print a single line if we see one
- # or more frames involving DMD.cpp.
- seen_DMD_frame = False
- for frame in fin:
- if re.match(r" ", frame):
- m = test_frame_re.match(frame)
- if m:
- seen_DMD_frame = True
- else:
- # We're past the stack trace.
- if seen_DMD_frame:
- print(" ... DMD.cpp", file=fout)
- print(frame, end='', file=fout)
- break
-
- else:
- # A line that needs no special handling. Copy it through.
- print(line, end='', file=fout)
-
- # Compare with expected output
-
- ret = subprocess.call(["diff", "-u", expected_name, filtered_name],
- stdout=open(diff_name, "w"))
-
- if ret == 0:
- print("TEST-PASS | {:} {:d} | ok".format(kind, i))
- else:
- print("TEST-UNEXPECTED-FAIL | {:} {:d} | mismatch".format(kind, i))
- print("Output files:")
- print("- " + fixed_name);
- print("- " + converted_name);
- print("- " + filtered_name);
- print("- " + diff_name);
-
-
-def main():
- if (len(sys.argv) != 2):
- print("usage:", sys.argv[0], "<topsrcdir>")
- sys.exit(1)
-
- src_dir = sys.argv[1]
-
- ntests = 4
- for i in range(1, ntests+1):
- test(src_dir, "reports", [], i)
- test(src_dir, "heap", ["--ignore-reports"], i)
-
-
-if __name__ == "__main__":
- main()
--- a/memory/replace/dmd/dmd.py
+++ b/memory/replace/dmd/dmd.py
@@ -97,17 +97,18 @@ def parseCommandLine():
return value
description = '''
Analyze heap data produced by DMD.
If no files are specified, read from stdin.
Write to stdout unless -o/--output is specified.
Stack traces are fixed to show function names, filenames and line numbers
unless --no-fix-stacks is specified; stack fixing modifies the original file
-and may take some time.
+and may take some time. If specified, the BREAKPAD_SYMBOLS_PATH environment
+variable is used to find breakpad symbols for stack fixing.
'''
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')
@@ -124,16 +125,19 @@ and may take some time.
help='ignore allocation functions at the start of traces')
p.add_argument('-b', '--show-all-block-sizes', action='store_true',
help='show individual block sizes for each record')
p.add_argument('--no-fix-stacks', action='store_true',
help='do not fix stacks')
+ p.add_argument('--filter-stacks-for-testing', action='store_true',
+ help='filter stack traces; only useful for testing purposes')
+
p.add_argument('input_file', type=argparse.FileType('r'))
return p.parse_args(sys.argv[1:])
# Fix stacks if necessary: first write the output to a tempfile, then replace
# the original file with it.
def fixStackTraces(args):
@@ -299,20 +303,36 @@ def main():
def plural(n):
return '' if n == 1 else 's'
# Prints to stdout, or to file if -o/--output was specified.
def out(*arguments, **kwargs):
print(*arguments, file=args.output, **kwargs)
def printStack(traceTable, frameTable, traceKey):
+ frameKeys = traceTable[traceKey]
+ fmt = ' #{:02d}{:}'
+
+ if args.filter_stacks_for_testing:
+ # If any frame has "DMD.cpp" or "replace_malloc.c" in its
+ # description -- as should be the case for every stack trace when
+ # running DMD in test mode -- we replace the entire trace with a
+ # single, predictable frame. There is too much variation in the
+ # stack traces across different machines and platforms to do more
+ # specific matching.
+ for frameKey in frameKeys:
+ frameDesc = frameTable[frameKey]
+ if 'DMD.cpp' in frameDesc or 'replace_malloc.c' in frameDesc:
+ out(fmt.format(1, ': ... DMD.cpp ...'))
+ return
+
# The frame number is always '#00' (see DMD.h for why), so we have to
# replace that with the correct frame number.
for n, frameKey in enumerate(traceTable[traceKey], start=1):
- out(' #{:02d}{:}'.format(n, frameTable[frameKey][3:]))
+ out(fmt.format(n, frameTable[frameKey][3:]))
def printRecords(recordKind, records, heapUsableSize):
RecordKind = recordKind.capitalize()
out(separator)
numRecords = len(records)
cmpRecords = sortByChoices[args.sort_by]
sortedRecords = sorted(records.values(), cmp=cmpRecords, reverse=True)
kindBlocks = 0
--- a/memory/replace/dmd/moz.build
+++ b/memory/replace/dmd/moz.build
@@ -27,8 +27,13 @@ if CONFIG['MOZ_OPTIMIZE']:
DEFINES['MOZ_OPTIMIZE'] = True
DISABLE_STL_WRAPPING = True
if CONFIG['OS_ARCH'] == 'WINNT':
OS_LIBS += [
'dbghelp',
]
+
+XPCSHELL_TESTS_MANIFESTS += [
+ 'test/xpcshell.ini',
+]
+
--- a/memory/replace/dmd/test/full-heap-expected2.txt
+++ b/memory/replace/dmd/test/full-heap-expected2.txt
@@ -7,116 +7,116 @@ Invocation {
#-----------------------------------------------------------------
Live {
1 block in heap block record 1 of 12
8,192 bytes (4,097 requested / 4,095 slop)
67.77% of the heap (67.77% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 2 of 12
1,024 bytes (1,023 requested / 1 slop)
8.47% of the heap (76.24% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
9 blocks in heap block record 3 of 12
1,008 bytes (900 requested / 108 slop)
8.34% of the heap (84.58% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
6 blocks in heap block record 4 of 12
528 bytes (528 requested / 0 slop)
4.37% of the heap (88.95% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
6 blocks in heap block record 5 of 12
528 bytes (528 requested / 0 slop)
4.37% of the heap (93.32% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 6 of 12
512 bytes (512 requested / 0 slop)
4.24% of the heap (97.55% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 7 of 12
80 bytes (79 requested / 1 slop)
0.66% of the heap (98.21% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 8 of 12
80 bytes (78 requested / 2 slop)
0.66% of the heap (98.87% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 9 of 12
80 bytes (77 requested / 3 slop)
0.66% of the heap (99.54% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 10 of 12
32 bytes (30 requested / 2 slop)
0.26% of the heap (99.80% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 11 of 12
16 bytes (10 requested / 6 slop)
0.13% of the heap (99.93% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 12 of 12
- 8 bytes (0 requested / 8 slop)
+ 8 bytes (8 requested / 0 slop)
0.07% of the heap (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Summary {
Total: 12,088 bytes in 30 blocks
}
--- a/memory/replace/dmd/test/full-heap-expected3.txt
+++ b/memory/replace/dmd/test/full-heap-expected3.txt
@@ -7,89 +7,89 @@ Invocation {
#-----------------------------------------------------------------
Live {
9 blocks in heap block record 1 of 9
1,008 bytes (900 requested / 108 slop)
35.49% of the heap (35.49% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
6 blocks in heap block record 2 of 9
528 bytes (528 requested / 0 slop)
18.59% of the heap (54.08% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
6 blocks in heap block record 3 of 9
528 bytes (528 requested / 0 slop)
18.59% of the heap (72.68% cumulative)
Allocated at {
- ... DMD.cpp
+ #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 {
- ... DMD.cpp
+ #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 {
- ... DMD.cpp
+ #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 {
- ... DMD.cpp
+ #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 {
- ... DMD.cpp
+ #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 {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 9 of 9
- 8 bytes (0 requested / 8 slop)
+ 8 bytes (8 requested / 0 slop)
0.28% of the heap (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Summary {
Total: 2,840 bytes in 27 blocks
}
--- a/memory/replace/dmd/test/full-heap-expected4.txt
+++ b/memory/replace/dmd/test/full-heap-expected4.txt
@@ -7,71 +7,71 @@ Invocation {
#-----------------------------------------------------------------
Live {
~4 blocks in heap block record 1 of 7
~512 bytes (~512 requested / ~0 slop)
35.96% of the heap (35.96% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 2 of 7
256 bytes (256 requested / 0 slop)
17.98% of the heap (53.93% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 3 of 7
144 bytes (144 requested / 0 slop)
10.11% of the heap (64.04% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
1 block in heap block record 4 of 7
128 bytes (128 requested / 0 slop)
8.99% of the heap (73.03% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
~1 block in heap block record 5 of 7
~128 bytes (~128 requested / ~0 slop)
8.99% of the heap (82.02% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
~1 block in heap block record 6 of 7
~128 bytes (~128 requested / ~0 slop)
8.99% of the heap (91.01% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Live {
~1 block in heap block record 7 of 7
~128 bytes (~128 requested / ~0 slop)
8.99% of the heap (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Summary {
Total: ~1,424 bytes in ~10 blocks
}
--- a/memory/replace/dmd/test/full-reports-expected2.txt
+++ b/memory/replace/dmd/test/full-reports-expected2.txt
@@ -8,248 +8,248 @@ Invocation {
#-----------------------------------------------------------------
Twice-reported {
1 block in heap block record 1 of 4
80 bytes (79 requested / 1 slop)
0.66% of the heap (0.66% cumulative)
29.41% of twice-reported (29.41% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported again at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Twice-reported {
1 block in heap block record 2 of 4
80 bytes (78 requested / 2 slop)
0.66% of the heap (1.32% cumulative)
29.41% of twice-reported (58.82% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported again at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Twice-reported {
1 block in heap block record 3 of 4
80 bytes (77 requested / 3 slop)
0.66% of the heap (1.99% cumulative)
29.41% of twice-reported (88.24% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported again at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Twice-reported {
1 block in heap block record 4 of 4
32 bytes (30 requested / 2 slop)
0.26% of the heap (2.25% cumulative)
11.76% of twice-reported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported again at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Unreported {
9 blocks in heap block record 1 of 3
1,008 bytes (900 requested / 108 slop)
8.34% of the heap (8.34% cumulative)
81.82% of unreported (81.82% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
2 blocks in heap block record 2 of 3
112 bytes (112 requested / 0 slop)
0.93% of the heap (9.27% cumulative)
9.09% of unreported (90.91% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
2 blocks in heap block record 3 of 3
112 bytes (112 requested / 0 slop)
0.93% of the heap (10.19% cumulative)
9.09% of unreported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Once-reported {
1 block in heap block record 1 of 11
8,192 bytes (4,097 requested / 4,095 slop)
67.77% of the heap (67.77% cumulative)
77.40% of once-reported (77.40% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 2 of 11
1,024 bytes (1,023 requested / 1 slop)
8.47% of the heap (76.24% cumulative)
9.67% of once-reported (87.07% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 3 of 11
512 bytes (512 requested / 0 slop)
4.24% of the heap (80.48% cumulative)
4.84% of once-reported (91.91% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
2 blocks in heap block record 4 of 11
240 bytes (240 requested / 0 slop)
1.99% of the heap (82.46% cumulative)
2.27% of once-reported (94.18% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
2 blocks in heap block record 5 of 11
240 bytes (240 requested / 0 slop)
1.99% of the heap (84.45% cumulative)
2.27% of once-reported (96.45% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 6 of 11
96 bytes (96 requested / 0 slop)
0.79% of the heap (85.24% cumulative)
0.91% of once-reported (97.35% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 7 of 11
96 bytes (96 requested / 0 slop)
0.79% of the heap (86.04% cumulative)
0.91% of once-reported (98.26% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 8 of 11
80 bytes (80 requested / 0 slop)
0.66% of the heap (86.70% cumulative)
0.76% of once-reported (99.02% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 9 of 11
80 bytes (80 requested / 0 slop)
0.66% of the heap (87.36% cumulative)
0.76% of once-reported (99.77% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 10 of 11
16 bytes (10 requested / 6 slop)
0.13% of the heap (87.49% cumulative)
0.15% of once-reported (99.92% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 11 of 11
- 8 bytes (0 requested / 8 slop)
+ 8 bytes (8 requested / 0 slop)
0.07% of the heap (87.56% cumulative)
0.08% of once-reported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Summary {
Total: 12,088 bytes (100.00%) in 30 blocks (100.00%)
Unreported: 1,232 bytes ( 10.19%) in 13 blocks ( 43.33%)
--- a/memory/replace/dmd/test/full-reports-expected3.txt
+++ b/memory/replace/dmd/test/full-reports-expected3.txt
@@ -8,125 +8,125 @@ Invocation {
#-----------------------------------------------------------------
Twice-reported {
1 block in heap block record 1 of 2
80 bytes (77 requested / 3 slop)
2.82% of the heap (2.82% cumulative)
90.91% of twice-reported (90.91% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported again at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Twice-reported {
1 block in heap block record 2 of 2
- 8 bytes (0 requested / 8 slop)
+ 8 bytes (8 requested / 0 slop)
0.28% of the heap (3.10% cumulative)
9.09% of twice-reported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported again at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Unreported {
9 blocks in heap block record 1 of 3
1,008 bytes (900 requested / 108 slop)
35.49% of the heap (35.49% cumulative)
48.84% of unreported (48.84% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
6 blocks in heap block record 2 of 3
528 bytes (528 requested / 0 slop)
18.59% of the heap (54.08% cumulative)
25.58% of unreported (74.42% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
6 blocks in heap block record 3 of 3
528 bytes (528 requested / 0 slop)
18.59% of the heap (72.68% cumulative)
25.58% of unreported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Once-reported {
1 block in heap block record 1 of 4
512 bytes (512 requested / 0 slop)
18.03% of the heap (18.03% cumulative)
74.42% of once-reported (74.42% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 2 of 4
80 bytes (79 requested / 1 slop)
2.82% of the heap (20.85% cumulative)
11.63% of once-reported (86.05% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 3 of 4
80 bytes (78 requested / 2 slop)
2.82% of the heap (23.66% cumulative)
11.63% of once-reported (97.67% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Once-reported {
1 block in heap block record 4 of 4
16 bytes (10 requested / 6 slop)
0.56% of the heap (24.23% cumulative)
2.33% of once-reported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
Reported at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
Summary {
Total: 2,840 bytes (100.00%) in 27 blocks (100.00%)
Unreported: 2,064 bytes ( 72.68%) in 21 blocks ( 77.78%)
--- a/memory/replace/dmd/test/full-reports-expected4.txt
+++ b/memory/replace/dmd/test/full-reports-expected4.txt
@@ -12,77 +12,77 @@ Invocation {
#-----------------------------------------------------------------
Unreported {
~4 blocks in heap block record 1 of 7
~512 bytes (~512 requested / ~0 slop)
35.96% of the heap (35.96% cumulative)
35.96% of unreported (35.96% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
1 block in heap block record 2 of 7
256 bytes (256 requested / 0 slop)
17.98% of the heap (53.93% cumulative)
17.98% of unreported (53.93% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
1 block in heap block record 3 of 7
144 bytes (144 requested / 0 slop)
10.11% of the heap (64.04% cumulative)
10.11% of unreported (64.04% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
1 block in heap block record 4 of 7
128 bytes (128 requested / 0 slop)
8.99% of the heap (73.03% cumulative)
8.99% of unreported (73.03% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
~1 block in heap block record 5 of 7
~128 bytes (~128 requested / ~0 slop)
8.99% of the heap (82.02% cumulative)
8.99% of unreported (82.02% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
~1 block in heap block record 6 of 7
~128 bytes (~128 requested / ~0 slop)
8.99% of the heap (91.01% cumulative)
8.99% of unreported (91.01% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
Unreported {
~1 block in heap block record 7 of 7
~128 bytes (~128 requested / ~0 slop)
8.99% of the heap (100.00% cumulative)
8.99% of unreported (100.00% cumulative)
Allocated at {
- ... DMD.cpp
+ #01: ... DMD.cpp ...
}
}
#-----------------------------------------------------------------
# no once-reported heap blocks
#-----------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/test_dmd.js
@@ -0,0 +1,87 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components
+
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+// The xpcshell test harness sets PYTHON so we can read it here.
+let gEnv = Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment);
+let gPythonName = gEnv.get("PYTHON");
+
+// If we're testing locally, the script is in "CurProcD". Otherwise, it is in
+// another location that we have to find.
+let gDmdScriptFile = FileUtils.getFile("CurProcD", ["dmd.py"]);
+if (!gDmdScriptFile.exists()) {
+ gDmdScriptFile = FileUtils.getFile("CurWorkD", []);
+ while (gDmdScriptFile.path.contains("xpcshell")) {
+ gDmdScriptFile = gDmdScriptFile.parent;
+ }
+ gDmdScriptFile.append("bin");
+ gDmdScriptFile.append("dmd.py");
+}
+
+function test(aJsonFile, aKind, aOptions, aN) {
+ // DMD writes the JSON files to CurWorkD, so we do likewise here with
+ // |actualFile| for consistency. It is removed once we've finished.
+ let expectedFile =
+ FileUtils.getFile("CurWorkD",
+ ["full-" + aKind + "-expected" + aN + ".txt"]);
+ let actualFile =
+ FileUtils.getFile("CurWorkD",
+ ["full-" + aKind + "-actual" + aN + ".txt"]);
+
+ // Run dmd.py on the JSON file, producing |actualFile|.
+
+ let pythonFile = new FileUtils.File(gPythonName);
+ let pythonProcess = Cc["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ pythonProcess.init(pythonFile);
+
+ let args = [
+ gDmdScriptFile.path,
+ "--filter-stacks-for-testing",
+ "-o", actualFile.path
+ ];
+ args = args.concat(aOptions);
+ args.push(aJsonFile.path);
+
+ pythonProcess.run(/* blocking = */true, args, args.length);
+
+ // Compare |expectedFile| with |actualFile|. Difference are printed to
+ // stdout.
+
+ let diffFile = new FileUtils.File("/usr/bin/diff");
+ let diffProcess = Cc["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ // XXX: this doesn't work on Windows (bug 1076446).
+ diffProcess.init(diffFile);
+
+ args = ["-u", expectedFile.path, actualFile.path];
+ diffProcess.run(/* blocking = */true, args, args.length);
+ let success = diffProcess.exitValue == 0;
+ ok(success, aKind + " " + aN);
+
+ actualFile.remove(true);
+}
+
+function run_test() {
+ // These tests do full end-to-end testing of DMD, i.e. both the C++ code that
+ // generates the JSON output, and the script that post-processes that output.
+ // The test relies on DMD's test mode executing beforehand, in order to
+ // produce the relevant JSON files.
+ //
+ // Run these synchronously, because test() updates the full*.json files
+ // in-place (to fix stacks) when it runs dmd.py, and that's not safe to do
+ // asynchronously.
+ for (let i = 1; i <= 4; i++) {
+ let jsonFile = FileUtils.getFile("CurWorkD", ["full" + i + ".json"]);
+ test(jsonFile, "heap", ["--ignore-reports"], i);
+ test(jsonFile, "reports", [], i);
+ jsonFile.remove(true);
+ }
+}
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/xpcshell.ini
@@ -0,0 +1,16 @@
+[DEFAULT]
+support-files =
+ full-heap-expected1.txt
+ full-heap-expected2.txt
+ full-heap-expected3.txt
+ full-heap-expected4.txt
+ full-reports-expected1.txt
+ full-reports-expected2.txt
+ full-reports-expected3.txt
+ full-reports-expected4.txt
+
+[test_dmd.js]
+dmd = true
+# XXX: bug 1076446 is open for running this test on Windows, and bug 1077230 is
+# open for running it on Mac.
+run-if = os == 'linux'
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -57,16 +57,20 @@ endif
ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
TEST_HARNESS_BINS += screenshot$(BIN_SUFFIX)
ifdef MOZ_METRO
TEST_HARNESS_BINS += metrotestharness$(BIN_SUFFIX)
endif
endif
+ifdef MOZ_DMD
+TEST_HARNESS_BINS += dmd.py
+endif
+
# Components / typelibs that don't get packaged with
# the build, but that we need for the test harness.
TEST_HARNESS_COMPONENTS := \
test_necko.xpt \
$(NULL)
# We need the test plugin as some tests rely on it
ifeq (Darwin,$(OS_TARGET))
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -601,16 +601,35 @@ class XPCShellTestThread(Thread):
cmdT = self.buildCmdTestFile(name)
args = self.xpcsRunArgs[:]
if 'debug' in self.test_object:
args.insert(0, '-d')
completeCmd = cmdH + cmdT + args
+ if self.test_object.get('dmd') == 'true':
+ if sys.platform.startswith('linux'):
+ preloadEnvVar = 'LD_PRELOAD'
+ libdmd = os.path.join(self.xrePath, 'libdmd.so')
+ elif sys.platform == 'osx' or sys.platform == 'darwin':
+ preloadEnvVar = 'DYLD_INSERT_LIBRARIES'
+ # self.xrePath is <prefix>/Contents/Resources.
+ # We need <prefix>/Contents/MacOS/libdmd.dylib.
+ contents_dir = os.path.dirname(self.xrePath)
+ libdmd = os.path.join(contents_dir, 'MacOS', 'libdmd.dylib')
+ elif sys.platform == 'win32':
+ preloadEnvVar = 'MOZ_REPLACE_MALLOC_LIB'
+ libdmd = os.path.join(self.xrePath, 'dmd.dll')
+
+ self.env['DMD'] = '--mode=test'
+ self.env['PYTHON'] = sys.executable
+ self.env['BREAKPAD_SYMBOLS_PATH'] = self.symbolsPath
+ self.env[preloadEnvVar] = libdmd
+
testTimeoutInterval = HARNESS_TIMEOUT
# Allow a test to request a multiple of the timeout if it is expected to take long
if 'requesttimeoutfactor' in self.test_object:
testTimeoutInterval *= int(self.test_object['requesttimeoutfactor'])
testTimer = None
if not self.interactive and not self.debuggerInfo:
testTimer = Timer(testTimeoutInterval, lambda: self.testTimeout(name, proc))