Backed out changeset 835fbe63da4a (bug 1076446) for perma failure in 10.8 mozilla-inbound debug test xpcshell
This commit is contained in:
@@ -227,6 +227,18 @@ InfallibleAllocPolicy::ExitOnFailure(const void* aP)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FpWriteFunc : public JSONWriteFunc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit FpWriteFunc(FILE* aFp) : mFp(aFp) {}
|
||||||
|
~FpWriteFunc() { fclose(mFp); }
|
||||||
|
|
||||||
|
void Write(const char* aStr) { fputs(aStr, mFp); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE* mFp;
|
||||||
|
};
|
||||||
|
|
||||||
static double
|
static double
|
||||||
Percent(size_t part, size_t whole)
|
Percent(size_t part, size_t whole)
|
||||||
{
|
{
|
||||||
@@ -277,11 +289,17 @@ class Options
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
Normal, // run normally
|
||||||
|
Test // do some basic correctness tests
|
||||||
|
};
|
||||||
|
|
||||||
char* mDMDEnvVar; // a saved copy, for later printing
|
char* mDMDEnvVar; // a saved copy, for later printing
|
||||||
|
|
||||||
NumOption<size_t> mSampleBelowSize;
|
NumOption<size_t> mSampleBelowSize;
|
||||||
NumOption<uint32_t> mMaxFrames;
|
NumOption<uint32_t> mMaxFrames;
|
||||||
bool mShowDumpStats;
|
bool mShowDumpStats;
|
||||||
|
Mode mMode;
|
||||||
|
|
||||||
void BadArg(const char* aArg);
|
void BadArg(const char* aArg);
|
||||||
static const char* ValueIfMatch(const char* aArg, const char* aOptionName);
|
static const char* ValueIfMatch(const char* aArg, const char* aOptionName);
|
||||||
@@ -298,7 +316,9 @@ public:
|
|||||||
size_t MaxFrames() const { return mMaxFrames.mActual; }
|
size_t MaxFrames() const { return mMaxFrames.mActual; }
|
||||||
size_t ShowDumpStats() const { return mShowDumpStats; }
|
size_t ShowDumpStats() const { return mShowDumpStats; }
|
||||||
|
|
||||||
void SetSampleBelowSize(size_t aSize) { mSampleBelowSize.mActual = aSize; }
|
void SetSampleBelowSize(size_t aN) { mSampleBelowSize.mActual = aN; }
|
||||||
|
|
||||||
|
bool IsTestMode() const { return mMode == Test; }
|
||||||
};
|
};
|
||||||
|
|
||||||
static Options *gOptions;
|
static Options *gOptions;
|
||||||
@@ -1258,7 +1278,8 @@ Options::Options(const char* aDMDEnvVar)
|
|||||||
: mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar)),
|
: mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar)),
|
||||||
mSampleBelowSize(4093, 100 * 100 * 1000),
|
mSampleBelowSize(4093, 100 * 100 * 1000),
|
||||||
mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames),
|
mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames),
|
||||||
mShowDumpStats(false)
|
mShowDumpStats(false),
|
||||||
|
mMode(Normal)
|
||||||
{
|
{
|
||||||
char* e = mDMDEnvVar;
|
char* e = mDMDEnvVar;
|
||||||
if (strcmp(e, "1") != 0) {
|
if (strcmp(e, "1") != 0) {
|
||||||
@@ -1293,6 +1314,11 @@ Options::Options(const char* aDMDEnvVar)
|
|||||||
} else if (GetBool(arg, "--show-dump-stats", &myBool)) {
|
} else if (GetBool(arg, "--show-dump-stats", &myBool)) {
|
||||||
mShowDumpStats = myBool;
|
mShowDumpStats = myBool;
|
||||||
|
|
||||||
|
} else if (strcmp(arg, "--mode=normal") == 0) {
|
||||||
|
mMode = Options::Normal;
|
||||||
|
} else if (strcmp(arg, "--mode=test") == 0) {
|
||||||
|
mMode = Options::Test;
|
||||||
|
|
||||||
} else if (strcmp(arg, "") == 0) {
|
} else if (strcmp(arg, "") == 0) {
|
||||||
// This can only happen if there is trailing whitespace. Ignore.
|
// This can only happen if there is trailing whitespace. Ignore.
|
||||||
MOZ_ASSERT(isEnd);
|
MOZ_ASSERT(isEnd);
|
||||||
@@ -1328,6 +1354,7 @@ Options::BadArg(const char* aArg)
|
|||||||
int(mMaxFrames.mMax),
|
int(mMaxFrames.mMax),
|
||||||
int(mMaxFrames.mDefault));
|
int(mMaxFrames.mDefault));
|
||||||
StatusMsg(" --show-dump-stats=<yes|no> Show stats about dumps? [no]\n");
|
StatusMsg(" --show-dump-stats=<yes|no> Show stats about dumps? [no]\n");
|
||||||
|
StatusMsg(" --mode=<normal|test> Mode of operation [normal]\n");
|
||||||
StatusMsg("\n");
|
StatusMsg("\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -1344,6 +1371,21 @@ NopStackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Note that fopen() can allocate.
|
||||||
|
static FILE*
|
||||||
|
OpenOutputFile(const char* aFilename)
|
||||||
|
{
|
||||||
|
FILE* fp = fopen(aFilename, "w");
|
||||||
|
if (!fp) {
|
||||||
|
StatusMsg("can't create %s file: %s\n", aFilename, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
|
||||||
|
UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4);
|
||||||
|
|
||||||
// WARNING: this function runs *very* early -- before all static initializers
|
// WARNING: this function runs *very* early -- before all static initializers
|
||||||
// have run. For this reason, non-scalar globals such as gStateLock and
|
// have run. For this reason, non-scalar globals such as gStateLock and
|
||||||
// gStackTraceTable are allocated dynamically (so we can guarantee their
|
// gStackTraceTable are allocated dynamically (so we can guarantee their
|
||||||
@@ -1397,7 +1439,31 @@ Init(const malloc_table_t* aMallocTable)
|
|||||||
gBlockTable->init(8192);
|
gBlockTable->init(8192);
|
||||||
}
|
}
|
||||||
|
|
||||||
gIsDMDRunning = true;
|
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("full-empty.json"));
|
||||||
|
auto f2 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-unsampled1.json"));
|
||||||
|
auto f3 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-unsampled2.json"));
|
||||||
|
auto f4 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-sampled.json"));
|
||||||
|
gIsDMDRunning = true;
|
||||||
|
|
||||||
|
StatusMsg("running test mode...\n");
|
||||||
|
RunTestMode(Move(f1), Move(f2), Move(f3), Move(f4));
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
@@ -1773,17 +1839,260 @@ AnalyzeReports(JSONWriter& aWriter)
|
|||||||
// Testing
|
// Testing
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
MOZ_EXPORT void
|
// This function checks that heap blocks that have the same stack trace but
|
||||||
SetSampleBelowSize(size_t aSize)
|
// different (or no) reporters get aggregated separately.
|
||||||
|
void Foo(int aSeven)
|
||||||
{
|
{
|
||||||
gOptions->SetSampleBelowSize(aSize);
|
char* a[6];
|
||||||
|
for (int i = 0; i < aSeven - 1; i++) {
|
||||||
|
a[i] = (char*) replace_malloc(128 - 16*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_EXPORT void
|
// This stops otherwise-unused variables from being optimized away.
|
||||||
ClearBlocks()
|
static void
|
||||||
|
UseItOrLoseIt(void* aPtr, int aSeven)
|
||||||
{
|
{
|
||||||
|
char buf[64];
|
||||||
|
int n = sprintf(buf, "%p\n", aPtr);
|
||||||
|
if (n == 20 + aSeven) {
|
||||||
|
fprintf(stderr, "well, that is surprising");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 replace_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 = nullptr;
|
||||||
|
for (i = 0; i < seven + 3; i++) {
|
||||||
|
a = (char*) replace_malloc(100);
|
||||||
|
UseItOrLoseIt(a, seven);
|
||||||
|
}
|
||||||
|
replace_free(a);
|
||||||
|
|
||||||
|
// 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*) replace_malloc(8);
|
||||||
|
Report(a2);
|
||||||
|
|
||||||
|
// AnalyzeReports 2: reported.
|
||||||
|
// AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
|
||||||
|
char* b = (char*) replace_malloc(10);
|
||||||
|
ReportOnAlloc(b);
|
||||||
|
|
||||||
|
// ReportOnAlloc, then freed.
|
||||||
|
// AnalyzeReports 2: freed, irrelevant.
|
||||||
|
// AnalyzeReports 3: freed, irrelevant.
|
||||||
|
char* b2 = (char*) replace_malloc(1);
|
||||||
|
ReportOnAlloc(b2);
|
||||||
|
replace_free(b2);
|
||||||
|
|
||||||
|
// AnalyzeReports 2: reported 4 times.
|
||||||
|
// AnalyzeReports 3: freed, irrelevant.
|
||||||
|
char* c = (char*) replace_calloc(10, 3);
|
||||||
|
Report(c);
|
||||||
|
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.
|
||||||
|
// AnalyzeReports 2: reported.
|
||||||
|
// AnalyzeReports 3: freed.
|
||||||
|
char* e = (char*) replace_malloc(4096);
|
||||||
|
e = (char*) replace_realloc(e, 4097);
|
||||||
|
Report(e);
|
||||||
|
|
||||||
|
// First realloc is like malloc; second realloc is shrinking.
|
||||||
|
// AnalyzeReports 2: reported.
|
||||||
|
// AnalyzeReports 3: re-reported.
|
||||||
|
char* e2 = (char*) replace_realloc(nullptr, 1024);
|
||||||
|
e2 = (char*) replace_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.
|
||||||
|
// AnalyzeReports 2: reported.
|
||||||
|
// AnalyzeReports 3: freed, irrelevant.
|
||||||
|
char* e3 = (char*) replace_realloc(nullptr, 1023);
|
||||||
|
//e3 = (char*) replace_realloc(e3, 0);
|
||||||
|
MOZ_ASSERT(e3);
|
||||||
|
Report(e3);
|
||||||
|
|
||||||
|
// AnalyzeReports 2: freed, irrelevant.
|
||||||
|
// AnalyzeReports 3: freed, irrelevant.
|
||||||
|
char* f = (char*) replace_malloc(64);
|
||||||
|
replace_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(seven);
|
||||||
|
Foo(seven);
|
||||||
|
|
||||||
|
// AnalyzeReports 2: twice-reported.
|
||||||
|
// AnalyzeReports 3: twice-reported.
|
||||||
|
char* g1 = (char*) replace_malloc(77);
|
||||||
|
ReportOnAlloc(g1);
|
||||||
|
ReportOnAlloc(g1);
|
||||||
|
|
||||||
|
// AnalyzeReports 2: twice-reported.
|
||||||
|
// AnalyzeReports 3: once-reported.
|
||||||
|
char* g2 = (char*) replace_malloc(78);
|
||||||
|
Report(g2);
|
||||||
|
ReportOnAlloc(g2);
|
||||||
|
|
||||||
|
// AnalyzeReports 2: twice-reported.
|
||||||
|
// AnalyzeReports 3: once-reported.
|
||||||
|
char* g3 = (char*) replace_malloc(79);
|
||||||
|
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, seven);
|
||||||
|
// XXX: posix_memalign doesn't work on B2G
|
||||||
|
//void* y;
|
||||||
|
//posix_memalign(&y, 128, 129); // rounds up to 256
|
||||||
|
//UseItOrLoseIt(y, seven);
|
||||||
|
// XXX: valloc doesn't work on Windows.
|
||||||
|
//void* z = valloc(1); // rounds up to 4096
|
||||||
|
//UseItOrLoseIt(z, seven);
|
||||||
|
//aligned_alloc(64, 256); // XXX: C11 only
|
||||||
|
|
||||||
|
// AnalyzeReports 2.
|
||||||
|
JSONWriter writer2(Move(aF2));
|
||||||
|
AnalyzeReports(writer2);
|
||||||
|
|
||||||
|
//---------
|
||||||
|
|
||||||
|
Report(a2);
|
||||||
|
Report(a2);
|
||||||
|
replace_free(c);
|
||||||
|
replace_free(e);
|
||||||
|
Report(e2);
|
||||||
|
replace_free(e3);
|
||||||
|
//replace_free(x);
|
||||||
|
//replace_free(y);
|
||||||
|
//replace_free(z);
|
||||||
|
|
||||||
|
// AnalyzeReports 3.
|
||||||
|
JSONWriter writer3(Move(aF3));
|
||||||
|
AnalyzeReports(writer3);
|
||||||
|
|
||||||
|
//---------
|
||||||
|
|
||||||
|
// Clear all knowledge of existing blocks to give us a clean slate.
|
||||||
gBlockTable->clear();
|
gBlockTable->clear();
|
||||||
gSmallBlockActualSizeCounter = 0;
|
|
||||||
|
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*) replace_malloc(128);
|
||||||
|
UseItOrLoseIt(s, seven);
|
||||||
|
|
||||||
|
// This exceeds the sample size, and so is reported exactly.
|
||||||
|
s = (char*) replace_malloc(144);
|
||||||
|
UseItOrLoseIt(s, seven);
|
||||||
|
|
||||||
|
// These together constitute exactly one sample.
|
||||||
|
for (int i = 0; i < seven + 9; i++) {
|
||||||
|
s = (char*) replace_malloc(8);
|
||||||
|
UseItOrLoseIt(s, seven);
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
|
||||||
|
|
||||||
|
// These fall 8 bytes short of a full sample.
|
||||||
|
for (int i = 0; i < seven + 8; i++) {
|
||||||
|
s = (char*) replace_malloc(8);
|
||||||
|
UseItOrLoseIt(s, seven);
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
|
||||||
|
|
||||||
|
// This exceeds the sample size, and so is recorded exactly.
|
||||||
|
s = (char*) replace_malloc(256);
|
||||||
|
UseItOrLoseIt(s, seven);
|
||||||
|
MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
|
||||||
|
|
||||||
|
// This gets more than to a full sample from the |i < 15| loop above.
|
||||||
|
s = (char*) replace_malloc(96);
|
||||||
|
UseItOrLoseIt(s, seven);
|
||||||
|
MOZ_ASSERT(gSmallBlockActualSizeCounter == 88);
|
||||||
|
|
||||||
|
// This gets to another full sample.
|
||||||
|
for (int i = 0; i < seven - 2; i++) {
|
||||||
|
s = (char*) replace_malloc(8);
|
||||||
|
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 <= seven + 1; i++) {
|
||||||
|
s = (char*) replace_malloc(i * 16);
|
||||||
|
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));
|
||||||
|
AnalyzeReports(writer4);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dmd
|
} // namespace dmd
|
||||||
|
|||||||
@@ -147,14 +147,6 @@ StatusMsg(const char* aFmt, ...);
|
|||||||
MOZ_EXPORT bool
|
MOZ_EXPORT bool
|
||||||
IsRunning();
|
IsRunning();
|
||||||
|
|
||||||
// Sets the sample-below size. Only used for testing purposes.
|
|
||||||
MOZ_EXPORT void
|
|
||||||
SetSampleBelowSize(size_t aSize);
|
|
||||||
|
|
||||||
// Clears all records of live allocations. Only used for testing purposes.
|
|
||||||
MOZ_EXPORT void
|
|
||||||
ClearBlocks();
|
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
} // namespace dmd
|
} // namespace dmd
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#! /usr/bin/env python
|
#! /usr/bin/python
|
||||||
#
|
#
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# 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
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -344,17 +344,15 @@ def main():
|
|||||||
fmt = ' #{:02d}{:}'
|
fmt = ' #{:02d}{:}'
|
||||||
|
|
||||||
if args.filter_stacks_for_testing:
|
if args.filter_stacks_for_testing:
|
||||||
# When running SmokeDMD.cpp, every stack trace should contain at
|
# If any frame has "DMD.cpp" or "replace_malloc.c" in its
|
||||||
# least one frame that contains 'DMD.cpp', from either |DMD.cpp| or
|
# description -- as should be the case for every stack trace when
|
||||||
# |SmokeDMD.cpp|. (Or 'dmd.cpp' on Windows.) If we see such a
|
# running DMD in test mode -- we replace the entire trace with a
|
||||||
# frame, we replace the entire stack trace with a single,
|
# single, predictable frame. There is too much variation in the
|
||||||
# predictable frame. There is too much variation in the stack
|
# stack traces across different machines and platforms to do more
|
||||||
# traces across different machines and platforms to do more precise
|
# specific matching.
|
||||||
# matching, but this level of matching will result in failure if
|
|
||||||
# stack fixing fails completely.
|
|
||||||
for frameKey in frameKeys:
|
for frameKey in frameKeys:
|
||||||
frameDesc = frameTable[frameKey]
|
frameDesc = frameTable[frameKey]
|
||||||
if 'DMD.cpp' in frameDesc or 'dmd.cpp' in frameDesc:
|
if 'DMD.cpp' in frameDesc or 'replace_malloc.c' in frameDesc:
|
||||||
out(fmt.format(1, ': ... DMD.cpp ...'))
|
out(fmt.format(1, ': ... DMD.cpp ...'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -33,5 +33,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||||||
'dbghelp',
|
'dbghelp',
|
||||||
]
|
]
|
||||||
|
|
||||||
TEST_DIRS += ['test']
|
XPCSHELL_TESTS_MANIFESTS += [
|
||||||
|
'test/xpcshell.ini',
|
||||||
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,325 +0,0 @@
|
|||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
|
||||||
|
|
||||||
// This program is used by the DMD xpcshell test. It is run under DMD and
|
|
||||||
// produces some output. The xpcshell test then post-processes and checks this
|
|
||||||
// output.
|
|
||||||
//
|
|
||||||
// Note that this file does not have "Test" or "test" in its name, because that
|
|
||||||
// will cause the build system to not record breakpad symbols for it, which
|
|
||||||
// will stop the post-processing (which includes stack fixing) from working
|
|
||||||
// correctly.
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
|
||||||
#include "mozilla/JSONWriter.h"
|
|
||||||
#include "mozilla/UniquePtr.h"
|
|
||||||
#include "DMD.h"
|
|
||||||
|
|
||||||
using mozilla::JSONWriter;
|
|
||||||
using mozilla::MakeUnique;
|
|
||||||
using namespace mozilla::dmd;
|
|
||||||
|
|
||||||
class FpWriteFunc : public mozilla::JSONWriteFunc
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit FpWriteFunc(const char* aFilename)
|
|
||||||
{
|
|
||||||
mFp = fopen(aFilename, "w");
|
|
||||||
if (!mFp) {
|
|
||||||
fprintf(stderr, "SmokeDMD: can't create %s file: %s\n",
|
|
||||||
aFilename, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~FpWriteFunc() { fclose(mFp); }
|
|
||||||
|
|
||||||
void Write(const char* aStr) { fputs(aStr, mFp); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE* mFp;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This stops otherwise-unused variables from being optimized away.
|
|
||||||
static void
|
|
||||||
UseItOrLoseIt(void* aPtr, int aSeven)
|
|
||||||
{
|
|
||||||
char buf[64];
|
|
||||||
int n = sprintf(buf, "%p\n", aPtr);
|
|
||||||
if (n == 20 + aSeven) {
|
|
||||||
fprintf(stderr, "well, that is surprising");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function checks that heap blocks that have the same stack trace but
|
|
||||||
// different (or no) reporters get aggregated separately.
|
|
||||||
void Foo(int aSeven)
|
|
||||||
{
|
|
||||||
char* a[6];
|
|
||||||
for (int i = 0; i < aSeven - 1; i++) {
|
|
||||||
a[i] = (char*) malloc(128 - 16*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
//---------
|
|
||||||
|
|
||||||
// AnalyzeReports 1. Zero for everything.
|
|
||||||
JSONWriter writer1(Move(f1));
|
|
||||||
AnalyzeReports(writer1);
|
|
||||||
|
|
||||||
//---------
|
|
||||||
|
|
||||||
// AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
|
|
||||||
// AnalyzeReports 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.
|
|
||||||
// AnalyzeReports 2: reported.
|
|
||||||
// AnalyzeReports 3: thrice-reported.
|
|
||||||
char* a2 = (char*) malloc(8);
|
|
||||||
Report(a2);
|
|
||||||
|
|
||||||
// AnalyzeReports 2: reported.
|
|
||||||
// AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
|
|
||||||
char* b = (char*) malloc(10);
|
|
||||||
ReportOnAlloc(b);
|
|
||||||
|
|
||||||
// ReportOnAlloc, then freed.
|
|
||||||
// AnalyzeReports 2: freed, irrelevant.
|
|
||||||
// AnalyzeReports 3: freed, irrelevant.
|
|
||||||
char* b2 = (char*) malloc(1);
|
|
||||||
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 < seven - 4; i++) {
|
|
||||||
Report(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnalyzeReports 2: ignored.
|
|
||||||
// AnalyzeReports 3: irrelevant.
|
|
||||||
Report((void*)(intptr_t)i);
|
|
||||||
|
|
||||||
// jemalloc rounds this up to 8192.
|
|
||||||
// AnalyzeReports 2: reported.
|
|
||||||
// AnalyzeReports 3: freed.
|
|
||||||
char* e = (char*) malloc(4096);
|
|
||||||
e = (char*) realloc(e, 4097);
|
|
||||||
Report(e);
|
|
||||||
|
|
||||||
// First realloc is like malloc; second realloc is shrinking.
|
|
||||||
// AnalyzeReports 2: reported.
|
|
||||||
// AnalyzeReports 3: re-reported.
|
|
||||||
char* e2 = (char*) realloc(nullptr, 1024);
|
|
||||||
e2 = (char*) realloc(e2, 512);
|
|
||||||
Report(e2);
|
|
||||||
|
|
||||||
// First realloc is like malloc; second realloc creates a min-sized block.
|
|
||||||
// XXX: on Windows, second realloc frees the block.
|
|
||||||
// AnalyzeReports 2: reported.
|
|
||||||
// AnalyzeReports 3: freed, irrelevant.
|
|
||||||
char* e3 = (char*) realloc(nullptr, 1023);
|
|
||||||
//e3 = (char*) realloc(e3, 0);
|
|
||||||
MOZ_ASSERT(e3);
|
|
||||||
Report(e3);
|
|
||||||
|
|
||||||
// AnalyzeReports 2: freed, irrelevant.
|
|
||||||
// AnalyzeReports 3: freed, irrelevant.
|
|
||||||
char* f = (char*) malloc(64);
|
|
||||||
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(seven);
|
|
||||||
|
|
||||||
// AnalyzeReports 2: twice-reported.
|
|
||||||
// AnalyzeReports 3: twice-reported.
|
|
||||||
char* g1 = (char*) malloc(77);
|
|
||||||
ReportOnAlloc(g1);
|
|
||||||
ReportOnAlloc(g1);
|
|
||||||
|
|
||||||
// AnalyzeReports 2: mixture of reported and unreported.
|
|
||||||
// AnalyzeReports 3: all unreported.
|
|
||||||
// Nb: this Foo() call is not adjacent to the previous one, because that has
|
|
||||||
// been seen to cause compilers to give them the same stacks, which can break
|
|
||||||
// the test.
|
|
||||||
Foo(seven);
|
|
||||||
|
|
||||||
// AnalyzeReports 2: twice-reported.
|
|
||||||
// AnalyzeReports 3: once-reported.
|
|
||||||
char* g2 = (char*) malloc(78);
|
|
||||||
Report(g2);
|
|
||||||
ReportOnAlloc(g2);
|
|
||||||
|
|
||||||
// AnalyzeReports 2: twice-reported.
|
|
||||||
// AnalyzeReports 3: once-reported.
|
|
||||||
char* g3 = (char*) malloc(79);
|
|
||||||
ReportOnAlloc(g3);
|
|
||||||
Report(g3);
|
|
||||||
|
|
||||||
// All the odd-ball ones.
|
|
||||||
// AnalyzeReports 2: all unreported.
|
|
||||||
// AnalyzeReports 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);
|
|
||||||
|
|
||||||
// AnalyzeReports 2.
|
|
||||||
JSONWriter writer2(Move(f2));
|
|
||||||
AnalyzeReports(writer2);
|
|
||||||
|
|
||||||
//---------
|
|
||||||
|
|
||||||
Report(a2);
|
|
||||||
Report(a2);
|
|
||||||
free(c);
|
|
||||||
free(e);
|
|
||||||
Report(e2);
|
|
||||||
free(e3);
|
|
||||||
//free(w);
|
|
||||||
//free(x);
|
|
||||||
//free(y);
|
|
||||||
//free(z);
|
|
||||||
|
|
||||||
// AnalyzeReports 3.
|
|
||||||
JSONWriter writer3(Move(f3));
|
|
||||||
AnalyzeReports(writer3);
|
|
||||||
|
|
||||||
//---------
|
|
||||||
|
|
||||||
// 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(144);
|
|
||||||
UseItOrLoseIt(s, seven);
|
|
||||||
|
|
||||||
// These together constitute exactly one sample.
|
|
||||||
for (int i = 0; i < seven + 9; i++) {
|
|
||||||
s = (char*) malloc(8);
|
|
||||||
UseItOrLoseIt(s, 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.
|
|
||||||
|
|
||||||
// AnalyzeReports 4.
|
|
||||||
JSONWriter writer4(Move(f4));
|
|
||||||
AnalyzeReports(writer4);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
RunTests();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 1
|
Sample-below size = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 128
|
Sample-below size = 128
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 1
|
Sample-below size = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 1
|
Sample-below size = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 1
|
Sample-below size = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 128
|
Sample-below size = 128
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 1
|
Sample-below size = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
|
|
||||||
Invocation {
|
Invocation {
|
||||||
$DMD = '1'
|
$DMD = '--mode=test'
|
||||||
Sample-below size = 1
|
Sample-below size = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
||||||
# vim: set filetype=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/.
|
|
||||||
|
|
||||||
SimplePrograms([
|
|
||||||
'SmokeDMD',
|
|
||||||
])
|
|
||||||
|
|
||||||
# See the comment at the top of SmokeDMD.cpp:RunTests().
|
|
||||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
|
||||||
CXXFLAGS += ['-Og-']
|
|
||||||
else:
|
|
||||||
CXXFLAGS += ['-O0']
|
|
||||||
|
|
||||||
DEFINES['MOZ_NO_MOZALLOC'] = True
|
|
||||||
|
|
||||||
DISABLE_STL_WRAPPING = True
|
|
||||||
|
|
||||||
USE_LIBS += ['dmd']
|
|
||||||
|
|
||||||
XPCSHELL_TESTS_MANIFESTS += [
|
|
||||||
'xpcshell.ini',
|
|
||||||
]
|
|
||||||
|
|
||||||
@@ -15,54 +15,16 @@ let gEnv = Cc["@mozilla.org/process/environment;1"]
|
|||||||
.getService(Ci.nsIEnvironment);
|
.getService(Ci.nsIEnvironment);
|
||||||
let gPythonName = gEnv.get("PYTHON");
|
let gPythonName = gEnv.get("PYTHON");
|
||||||
|
|
||||||
// If we're testing locally, the executable file is in "CurProcD". Otherwise,
|
// If we're testing locally, the script is in "CurProcD". Otherwise, it is in
|
||||||
// it is in another location that we have to find.
|
// another location that we have to find.
|
||||||
function getExecutable(aFilename) {
|
let gDmdScriptFile = FileUtils.getFile("CurProcD", ["dmd.py"]);
|
||||||
let file = FileUtils.getFile("CurProcD", [aFilename]);
|
if (!gDmdScriptFile.exists()) {
|
||||||
if (!file.exists()) {
|
gDmdScriptFile = FileUtils.getFile("CurWorkD", []);
|
||||||
file = FileUtils.getFile("CurWorkD", []);
|
while (gDmdScriptFile.path.contains("xpcshell")) {
|
||||||
while (file.path.contains("xpcshell")) {
|
gDmdScriptFile = gDmdScriptFile.parent;
|
||||||
file = file.parent;
|
|
||||||
}
|
|
||||||
file.append("bin");
|
|
||||||
file.append(aFilename);
|
|
||||||
}
|
}
|
||||||
return file;
|
gDmdScriptFile.append("bin");
|
||||||
}
|
gDmdScriptFile.append("dmd.py");
|
||||||
|
|
||||||
let gIsWindows = Cc["@mozilla.org/xre/app-info;1"]
|
|
||||||
.getService(Ci.nsIXULRuntime).OS === "WINNT";
|
|
||||||
let gDmdTestFile = getExecutable("SmokeDMD" + (gIsWindows ? ".exe" : ""));
|
|
||||||
|
|
||||||
let gDmdScriptFile = getExecutable("dmd.py");
|
|
||||||
|
|
||||||
function readFile(aFile) {
|
|
||||||
var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
|
|
||||||
.createInstance(Ci.nsIFileInputStream);
|
|
||||||
var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
|
|
||||||
.createInstance(Ci.nsIConverterInputStream);
|
|
||||||
fstream.init(aFile, -1, 0, 0);
|
|
||||||
cstream.init(fstream, "UTF-8", 0, 0);
|
|
||||||
|
|
||||||
var data = "";
|
|
||||||
let (str = {}) {
|
|
||||||
let read = 0;
|
|
||||||
do {
|
|
||||||
// Read as much as we can and put it in str.value.
|
|
||||||
read = cstream.readString(0xffffffff, str);
|
|
||||||
data += str.value;
|
|
||||||
} while (read != 0);
|
|
||||||
}
|
|
||||||
cstream.close(); // this closes fstream
|
|
||||||
return data.replace(/\r/g, ""); // normalize line endings
|
|
||||||
}
|
|
||||||
|
|
||||||
function runProcess(aExeFile, aArgs) {
|
|
||||||
let process = Cc["@mozilla.org/process/util;1"]
|
|
||||||
.createInstance(Components.interfaces.nsIProcess);
|
|
||||||
process.init(aExeFile);
|
|
||||||
process.run(/* blocking = */true, aArgs, aArgs.length);
|
|
||||||
return process.exitValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function test(aJsonFile, aPrefix, aOptions) {
|
function test(aJsonFile, aPrefix, aOptions) {
|
||||||
@@ -73,6 +35,11 @@ function test(aJsonFile, aPrefix, aOptions) {
|
|||||||
|
|
||||||
// Run dmd.py on the JSON file, producing |actualFile|.
|
// 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 = [
|
let args = [
|
||||||
gDmdScriptFile.path,
|
gDmdScriptFile.path,
|
||||||
"--filter-stacks-for-testing",
|
"--filter-stacks-for-testing",
|
||||||
@@ -81,35 +48,20 @@ function test(aJsonFile, aPrefix, aOptions) {
|
|||||||
args = args.concat(aOptions);
|
args = args.concat(aOptions);
|
||||||
args.push(aJsonFile.path);
|
args.push(aJsonFile.path);
|
||||||
|
|
||||||
runProcess(new FileUtils.File(gPythonName), args);
|
pythonProcess.run(/* blocking = */true, args, args.length);
|
||||||
|
|
||||||
// Compare |expectedFile| with |actualFile|. We produce nice diffs with
|
// Compare |expectedFile| with |actualFile|. Difference are printed to
|
||||||
// /usr/bin/diff on systems that have it (Mac and Linux). Otherwise (Windows)
|
// stdout.
|
||||||
// we do a string compare of the file contents and then print them both if
|
|
||||||
// they don't match.
|
|
||||||
|
|
||||||
let success;
|
let diffFile = new FileUtils.File("/usr/bin/diff");
|
||||||
try {
|
let diffProcess = Cc["@mozilla.org/process/util;1"]
|
||||||
let rv = runProcess(new FileUtils.File("/usr/bin/diff"),
|
.createInstance(Components.interfaces.nsIProcess);
|
||||||
["-u", expectedFile.path, actualFile.path]);
|
// XXX: this doesn't work on Windows (bug 1076446).
|
||||||
success = rv == 0;
|
diffProcess.init(diffFile);
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
let expectedData = readFile(expectedFile);
|
|
||||||
let actualData = readFile(actualFile);
|
|
||||||
success = expectedData === actualData;
|
|
||||||
if (!success) {
|
|
||||||
expectedData = expectedData.split("\n");
|
|
||||||
actualData = actualData.split("\n");
|
|
||||||
for (let i = 0; i < expectedData.length; i++) {
|
|
||||||
print("EXPECTED:" + expectedData[i]);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < actualData.length; i++) {
|
|
||||||
print(" ACTUAL:" + actualData[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
args = ["-u", expectedFile.path, actualFile.path];
|
||||||
|
diffProcess.run(/* blocking = */true, args, args.length);
|
||||||
|
let success = diffProcess.exitValue == 0;
|
||||||
ok(success, aPrefix);
|
ok(success, aPrefix);
|
||||||
|
|
||||||
actualFile.remove(true);
|
actualFile.remove(true);
|
||||||
@@ -120,16 +72,12 @@ function run_test() {
|
|||||||
|
|
||||||
// These tests do full end-to-end testing of DMD, i.e. both the C++ code that
|
// 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.
|
// 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
|
// 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
|
// in-place (to fix stacks) when it runs dmd.py, and that's not safe to do
|
||||||
// asynchronously.
|
// 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"];
|
let fullTestNames = ["empty", "unsampled1", "unsampled2", "sampled"];
|
||||||
for (let i = 0; i < fullTestNames.length; i++) {
|
for (let i = 0; i < fullTestNames.length; i++) {
|
||||||
let name = fullTestNames[i];
|
let name = fullTestNames[i];
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ support-files =
|
|||||||
script-show-all-block-sizes-expected.txt
|
script-show-all-block-sizes-expected.txt
|
||||||
|
|
||||||
# Bug 1077230 explains why this test is disabled on Mac 10.6.
|
# 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
|
# Bug 1076446 is open for getting this test working on on Windows.
|
||||||
# (WinXP) and 6.1 (Win7), but not 6.2 (Win8).
|
|
||||||
[test_dmd.js]
|
[test_dmd.js]
|
||||||
dmd = true
|
dmd = true
|
||||||
run-if = os == 'linux' || os == 'mac' && os_version != '10.6' || os == 'win' && (os_version == '5.1' || os_version == '6.1')
|
run-if = os == 'linux' || os == 'mac' && os_version != '10.6'
|
||||||
|
|||||||
@@ -828,9 +828,11 @@ class RunProgram(MachCommandBase):
|
|||||||
help='The maximum depth of stack traces. The default and maximum is 24.')
|
help='The maximum depth of stack traces. The default and maximum is 24.')
|
||||||
@CommandArgument('--show-dump-stats', action='store_true', group='DMD',
|
@CommandArgument('--show-dump-stats', action='store_true', group='DMD',
|
||||||
help='Show stats when doing dumps.')
|
help='Show stats when doing dumps.')
|
||||||
|
@CommandArgument('--mode', choices=['normal', 'test'], group='DMD',
|
||||||
|
help='Mode of operation. The default is normal.')
|
||||||
def run(self, params, remote, background, noprofile, debug, debugger,
|
def run(self, params, remote, background, noprofile, debug, debugger,
|
||||||
debugparams, slowscript, dmd, sample_below, max_frames,
|
debugparams, slowscript, dmd, sample_below, max_frames,
|
||||||
show_dump_stats):
|
show_dump_stats, mode):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
binpath = self.get_binary_path('app')
|
binpath = self.get_binary_path('app')
|
||||||
@@ -900,6 +902,8 @@ class RunProgram(MachCommandBase):
|
|||||||
dmd_params.append('--max-frames=' + max_frames)
|
dmd_params.append('--max-frames=' + max_frames)
|
||||||
if show_dump_stats:
|
if show_dump_stats:
|
||||||
dmd_params.append('--show-dump-stats=yes')
|
dmd_params.append('--show-dump-stats=yes')
|
||||||
|
if mode:
|
||||||
|
dmd_params.append('--mode=' + mode)
|
||||||
|
|
||||||
if dmd_params:
|
if dmd_params:
|
||||||
dmd_env_var = " ".join(dmd_params)
|
dmd_env_var = " ".join(dmd_params)
|
||||||
|
|||||||
@@ -63,10 +63,7 @@ endif
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef MOZ_DMD
|
ifdef MOZ_DMD
|
||||||
TEST_HARNESS_BINS += \
|
TEST_HARNESS_BINS += dmd.py
|
||||||
dmd.py \
|
|
||||||
SmokeDMD$(BIN_SUFFIX) \
|
|
||||||
$(NULL)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Components / typelibs that don't get packaged with
|
# Components / typelibs that don't get packaged with
|
||||||
|
|||||||
@@ -620,10 +620,10 @@ class XPCShellTestThread(Thread):
|
|||||||
preloadEnvVar = 'MOZ_REPLACE_MALLOC_LIB'
|
preloadEnvVar = 'MOZ_REPLACE_MALLOC_LIB'
|
||||||
libdmd = os.path.join(self.xrePath, 'dmd.dll')
|
libdmd = os.path.join(self.xrePath, 'dmd.dll')
|
||||||
|
|
||||||
|
self.env['DMD'] = '--mode=test'
|
||||||
self.env['PYTHON'] = sys.executable
|
self.env['PYTHON'] = sys.executable
|
||||||
self.env['BREAKPAD_SYMBOLS_PATH'] = self.symbolsPath
|
self.env['BREAKPAD_SYMBOLS_PATH'] = self.symbolsPath
|
||||||
self.env['DMD_PRELOAD_VAR'] = preloadEnvVar
|
self.env[preloadEnvVar] = libdmd
|
||||||
self.env['DMD_PRELOAD_VALUE'] = libdmd
|
|
||||||
|
|
||||||
testTimeoutInterval = HARNESS_TIMEOUT
|
testTimeoutInterval = HARNESS_TIMEOUT
|
||||||
# Allow a test to request a multiple of the timeout if it is expected to take long
|
# Allow a test to request a multiple of the timeout if it is expected to take long
|
||||||
|
|||||||
@@ -596,11 +596,8 @@ NO_PKG_FILES += \
|
|||||||
# If a manifest has not been supplied, the following
|
# If a manifest has not been supplied, the following
|
||||||
# files should be excluded from the package too
|
# files should be excluded from the package too
|
||||||
ifndef MOZ_PKG_MANIFEST
|
ifndef MOZ_PKG_MANIFEST
|
||||||
NO_PKG_FILES += ssltunnel*
|
NO_PKG_FILES += \
|
||||||
endif
|
ssltunnel*
|
||||||
|
|
||||||
ifdef MOZ_DMD
|
|
||||||
NO_PKG_FILES += SmokeDMD
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# browser/locales/Makefile uses this makefile for its variable defs, but
|
# browser/locales/Makefile uses this makefile for its variable defs, but
|
||||||
|
|||||||
Reference in New Issue
Block a user