From 07aaa8641afaaf84ff30ea37df8b4f0a8739d1e3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 28 Sep 2014 20:50:52 -0700 Subject: [PATCH] Bug 1073312 - Test DMD on TBPL (Linux-only). r=glandium. --HG-- extra : rebase_source : c35cc2bfd6720db35ece715dc7ef900ab5864409 --- memory/replace/dmd/DMD.cpp | 117 +++++++++------- memory/replace/dmd/check_test_output.py | 127 ------------------ memory/replace/dmd/dmd.py | 24 +++- memory/replace/dmd/moz.build | 5 + .../replace/dmd/test/full-heap-expected2.txt | 26 ++-- .../replace/dmd/test/full-heap-expected3.txt | 20 +-- .../replace/dmd/test/full-heap-expected4.txt | 14 +- .../dmd/test/full-reports-expected2.txt | 76 +++++------ .../dmd/test/full-reports-expected3.txt | 36 ++--- .../dmd/test/full-reports-expected4.txt | 14 +- memory/replace/dmd/test/test_dmd.js | 87 ++++++++++++ memory/replace/dmd/test/xpcshell.ini | 16 +++ testing/mochitest/Makefile.in | 4 + testing/xpcshell/runxpcshelltests.py | 19 +++ 14 files changed, 316 insertions(+), 269 deletions(-) delete mode 100755 memory/replace/dmd/check_test_output.py create mode 100644 memory/replace/dmd/test/test_dmd.js create mode 100644 memory/replace/dmd/test/xpcshell.ini diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp index d889cf463ef..dcefbf9500a 100644 --- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -1402,18 +1402,16 @@ Init(const malloc_table_t* aMallocTable) // - 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_(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 @@ -1445,6 +1443,10 @@ Init(const malloc_table_t* aMallocTable) // 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(OpenOutputFile("full1.json")); auto f2 = MakeUnique(OpenOutputFile("full2.json")); auto f3 = MakeUnique(OpenOutputFile("full3.json")); @@ -1453,11 +1455,15 @@ Init(const malloc_table_t* aMallocTable) 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"); - gIsDMDRunning = true; + // Continue running so that the xpcshell test can complete, but DMD no + // longer needs to be running. + gIsDMDRunning = false; + + } else { + gIsDMDRunning = true; + } } //--------------------------------------------------------------------------- @@ -1835,36 +1841,52 @@ AnalyzeReports(JSONWriter& aWriter) // 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 aF1, UniquePtr aF2, UniquePtr aF3, UniquePtr 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); @@ -1879,17 +1901,18 @@ RunTestMode(UniquePtr aF1, UniquePtr aF2, // 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[]. @@ -1909,7 +1932,7 @@ RunTestMode(UniquePtr aF1, UniquePtr aF2, // 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); } @@ -1951,8 +1974,8 @@ RunTestMode(UniquePtr aF1, UniquePtr aF2, // 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. @@ -1977,14 +2000,14 @@ RunTestMode(UniquePtr aF1, UniquePtr aF2, // 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. @@ -2019,49 +2042,49 @@ RunTestMode(UniquePtr aF1, UniquePtr aF2, // 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); diff --git a/memory/replace/dmd/check_test_output.py b/memory/replace/dmd/check_test_output.py deleted file mode 100755 index ac04cebb55b..00000000000 --- 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], "") - 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() diff --git a/memory/replace/dmd/dmd.py b/memory/replace/dmd/dmd.py index 7579884f90f..a1881d1101d 100755 --- a/memory/replace/dmd/dmd.py +++ b/memory/replace/dmd/dmd.py @@ -102,7 +102,8 @@ 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) @@ -129,6 +130,9 @@ and may take some time. 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:]) @@ -304,10 +308,26 @@ def main(): 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() diff --git a/memory/replace/dmd/moz.build b/memory/replace/dmd/moz.build index 25fe052c2b1..f8121eeac2a 100644 --- a/memory/replace/dmd/moz.build +++ b/memory/replace/dmd/moz.build @@ -32,3 +32,8 @@ if CONFIG['OS_ARCH'] == 'WINNT': OS_LIBS += [ 'dbghelp', ] + +XPCSHELL_TESTS_MANIFESTS += [ + 'test/xpcshell.ini', +] + diff --git a/memory/replace/dmd/test/full-heap-expected2.txt b/memory/replace/dmd/test/full-heap-expected2.txt index 68a5e1fcfad..16f65c56e96 100644 --- a/memory/replace/dmd/test/full-heap-expected2.txt +++ b/memory/replace/dmd/test/full-heap-expected2.txt @@ -12,7 +12,7 @@ Live { 8,192 bytes (4,097 requested / 4,095 slop) 67.77% of the heap (67.77% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -21,7 +21,7 @@ Live { 1,024 bytes (1,023 requested / 1 slop) 8.47% of the heap (76.24% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -30,7 +30,7 @@ Live { 1,008 bytes (900 requested / 108 slop) 8.34% of the heap (84.58% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -39,7 +39,7 @@ Live { 528 bytes (528 requested / 0 slop) 4.37% of the heap (88.95% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -48,7 +48,7 @@ Live { 528 bytes (528 requested / 0 slop) 4.37% of the heap (93.32% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -57,7 +57,7 @@ Live { 512 bytes (512 requested / 0 slop) 4.24% of the heap (97.55% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -66,7 +66,7 @@ Live { 80 bytes (79 requested / 1 slop) 0.66% of the heap (98.21% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -75,7 +75,7 @@ Live { 80 bytes (78 requested / 2 slop) 0.66% of the heap (98.87% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -84,7 +84,7 @@ Live { 80 bytes (77 requested / 3 slop) 0.66% of the heap (99.54% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -93,7 +93,7 @@ Live { 32 bytes (30 requested / 2 slop) 0.26% of the heap (99.80% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -102,16 +102,16 @@ Live { 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 ... } } diff --git a/memory/replace/dmd/test/full-heap-expected3.txt b/memory/replace/dmd/test/full-heap-expected3.txt index 9a25cd9349d..49a329f32f6 100644 --- a/memory/replace/dmd/test/full-heap-expected3.txt +++ b/memory/replace/dmd/test/full-heap-expected3.txt @@ -12,7 +12,7 @@ Live { 1,008 bytes (900 requested / 108 slop) 35.49% of the heap (35.49% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -21,7 +21,7 @@ Live { 528 bytes (528 requested / 0 slop) 18.59% of the heap (54.08% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -30,7 +30,7 @@ Live { 528 bytes (528 requested / 0 slop) 18.59% of the heap (72.68% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -39,7 +39,7 @@ Live { 512 bytes (512 requested / 0 slop) 18.03% of the heap (90.70% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -48,7 +48,7 @@ Live { 80 bytes (79 requested / 1 slop) 2.82% of the heap (93.52% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -57,7 +57,7 @@ Live { 80 bytes (78 requested / 2 slop) 2.82% of the heap (96.34% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -66,7 +66,7 @@ Live { 80 bytes (77 requested / 3 slop) 2.82% of the heap (99.15% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -75,16 +75,16 @@ Live { 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 ... } } diff --git a/memory/replace/dmd/test/full-heap-expected4.txt b/memory/replace/dmd/test/full-heap-expected4.txt index 1093eaa6a63..ec7fc6baf05 100644 --- a/memory/replace/dmd/test/full-heap-expected4.txt +++ b/memory/replace/dmd/test/full-heap-expected4.txt @@ -12,7 +12,7 @@ Live { ~512 bytes (~512 requested / ~0 slop) 35.96% of the heap (35.96% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -21,7 +21,7 @@ Live { 256 bytes (256 requested / 0 slop) 17.98% of the heap (53.93% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -30,7 +30,7 @@ Live { 144 bytes (144 requested / 0 slop) 10.11% of the heap (64.04% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -39,7 +39,7 @@ Live { 128 bytes (128 requested / 0 slop) 8.99% of the heap (73.03% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -48,7 +48,7 @@ Live { ~128 bytes (~128 requested / ~0 slop) 8.99% of the heap (82.02% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -57,7 +57,7 @@ Live { ~128 bytes (~128 requested / ~0 slop) 8.99% of the heap (91.01% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -66,7 +66,7 @@ Live { ~128 bytes (~128 requested / ~0 slop) 8.99% of the heap (100.00% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } diff --git a/memory/replace/dmd/test/full-reports-expected2.txt b/memory/replace/dmd/test/full-reports-expected2.txt index ad784294da1..e5c16817cab 100644 --- a/memory/replace/dmd/test/full-reports-expected2.txt +++ b/memory/replace/dmd/test/full-reports-expected2.txt @@ -13,13 +13,13 @@ Twice-reported { 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 ... } } @@ -29,13 +29,13 @@ Twice-reported { 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 ... } } @@ -45,13 +45,13 @@ Twice-reported { 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 ... } } @@ -61,13 +61,13 @@ Twice-reported { 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 ... } } @@ -79,7 +79,7 @@ Unreported { 8.34% of the heap (8.34% cumulative) 81.82% of unreported (81.82% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -89,7 +89,7 @@ Unreported { 0.93% of the heap (9.27% cumulative) 9.09% of unreported (90.91% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -99,7 +99,7 @@ Unreported { 0.93% of the heap (10.19% cumulative) 9.09% of unreported (100.00% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -111,10 +111,10 @@ Once-reported { 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 ... } } @@ -124,10 +124,10 @@ Once-reported { 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 ... } } @@ -137,10 +137,10 @@ Once-reported { 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 ... } } @@ -150,10 +150,10 @@ Once-reported { 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 ... } } @@ -163,10 +163,10 @@ Once-reported { 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 ... } } @@ -176,10 +176,10 @@ Once-reported { 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 ... } } @@ -189,10 +189,10 @@ Once-reported { 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 ... } } @@ -202,10 +202,10 @@ Once-reported { 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 ... } } @@ -215,10 +215,10 @@ Once-reported { 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 ... } } @@ -228,23 +228,23 @@ Once-reported { 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 ... } } diff --git a/memory/replace/dmd/test/full-reports-expected3.txt b/memory/replace/dmd/test/full-reports-expected3.txt index 9f722852ba0..7457aa3d473 100644 --- a/memory/replace/dmd/test/full-reports-expected3.txt +++ b/memory/replace/dmd/test/full-reports-expected3.txt @@ -13,29 +13,29 @@ Twice-reported { 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 ... } } @@ -47,7 +47,7 @@ Unreported { 35.49% of the heap (35.49% cumulative) 48.84% of unreported (48.84% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -57,7 +57,7 @@ Unreported { 18.59% of the heap (54.08% cumulative) 25.58% of unreported (74.42% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -67,7 +67,7 @@ Unreported { 18.59% of the heap (72.68% cumulative) 25.58% of unreported (100.00% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -79,10 +79,10 @@ Once-reported { 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 ... } } @@ -92,10 +92,10 @@ Once-reported { 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 ... } } @@ -105,10 +105,10 @@ Once-reported { 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 ... } } @@ -118,10 +118,10 @@ Once-reported { 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 ... } } diff --git a/memory/replace/dmd/test/full-reports-expected4.txt b/memory/replace/dmd/test/full-reports-expected4.txt index 2d82dd5c0c8..3a7878e60ab 100644 --- a/memory/replace/dmd/test/full-reports-expected4.txt +++ b/memory/replace/dmd/test/full-reports-expected4.txt @@ -17,7 +17,7 @@ Unreported { 35.96% of the heap (35.96% cumulative) 35.96% of unreported (35.96% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -27,7 +27,7 @@ Unreported { 17.98% of the heap (53.93% cumulative) 17.98% of unreported (53.93% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -37,7 +37,7 @@ Unreported { 10.11% of the heap (64.04% cumulative) 10.11% of unreported (64.04% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -47,7 +47,7 @@ Unreported { 8.99% of the heap (73.03% cumulative) 8.99% of unreported (73.03% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -57,7 +57,7 @@ Unreported { 8.99% of the heap (82.02% cumulative) 8.99% of unreported (82.02% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -67,7 +67,7 @@ Unreported { 8.99% of the heap (91.01% cumulative) 8.99% of unreported (91.01% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } @@ -77,7 +77,7 @@ Unreported { 8.99% of the heap (100.00% cumulative) 8.99% of unreported (100.00% cumulative) Allocated at { - ... DMD.cpp + #01: ... DMD.cpp ... } } diff --git a/memory/replace/dmd/test/test_dmd.js b/memory/replace/dmd/test/test_dmd.js new file mode 100644 index 00000000000..6b19c552f19 --- /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); + } +} diff --git a/memory/replace/dmd/test/xpcshell.ini b/memory/replace/dmd/test/xpcshell.ini new file mode 100644 index 00000000000..5d3f7a619df --- /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' diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index 1df15889ff3..b28e8ebb0e0 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -62,6 +62,10 @@ 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 := \ diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index d3e92a21ca7..a4b963538a4 100755 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -606,6 +606,25 @@ class XPCShellTestThread(Thread): 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 /Contents/Resources. + # We need /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: