mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-25 17:43:44 +00:00
7def0f9cd3
This also effectively changes how DMD is enabled from requiring both replace-malloc initialization and the DMD environment variable to requiring only the former. The DMD environment variable can still be used to specify options, but not to disable entirely. This however doesn't touch all the parts that do enable DMD by setting the DMD environment variable to 1, so the code to handle this value is kept.
247 lines
6.2 KiB
C++
247 lines
6.2 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: sw=4 ts=4 et :
|
|
* 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/. */
|
|
|
|
// Avoid DMD-specific parts of MOZ_DEFINE_MALLOC_SIZE_OF
|
|
#undef MOZ_DMD
|
|
|
|
#include "TestHarness.h"
|
|
#include "nsIMemoryReporter.h"
|
|
|
|
//#define OLD_API
|
|
|
|
#define PASS() \
|
|
do { \
|
|
passed(__FUNCTION__); \
|
|
return NS_OK; \
|
|
} while (0)
|
|
|
|
#define FAIL(why) \
|
|
do { \
|
|
fail("%s | %s - %s", __FILE__, __FUNCTION__, why); \
|
|
return NS_ERROR_FAILURE; \
|
|
} while (0)
|
|
|
|
#ifdef OLD_API
|
|
# include "nsAutoLock.h"
|
|
typedef PRLock* moz_lock_t;
|
|
# define NEWLOCK(n) nsAutoLock::NewLock(n)
|
|
# define DELETELOCK(v) nsAutoLock::DestroyLock(v)
|
|
# define AUTOLOCK(v, l) nsAutoLock v(l)
|
|
#else
|
|
# include "mozilla/Mutex.h"
|
|
typedef mozilla::Mutex* moz_lock_t;
|
|
# define NEWLOCK(n) new mozilla::Mutex(n)
|
|
# define DELETELOCK(v) delete (v)
|
|
# define AUTOLOCK(v, l) mozilla::MutexAutoLock v(*l)
|
|
#endif
|
|
|
|
// def/undef these to run particular tests.
|
|
#undef DD_TEST1
|
|
#undef DD_TEST2
|
|
#undef DD_TEST3
|
|
#undef DD_TEST4
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef DD_TEST1
|
|
|
|
static void
|
|
AllocLockRecurseUnlockFree(int i)
|
|
{
|
|
if (0 == i)
|
|
return;
|
|
|
|
moz_lock_t lock = NEWLOCK("deadlockDetector.scalability.t1");
|
|
{
|
|
AUTOLOCK(_, lock);
|
|
AllocLockRecurseUnlockFree(i - 1);
|
|
}
|
|
DELETELOCK(lock);
|
|
}
|
|
|
|
// This test creates a resource dependency chain N elements long, then
|
|
// frees all the resources in the chain.
|
|
static nsresult
|
|
LengthNDepChain(int N)
|
|
{
|
|
AllocLockRecurseUnlockFree(N);
|
|
PASS();
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef DD_TEST2
|
|
|
|
// This test creates a single lock that is ordered < N resources, then
|
|
// repeatedly exercises this order k times.
|
|
static nsresult
|
|
OneLockNDeps(const int N, const int K)
|
|
{
|
|
moz_lock_t lock = NEWLOCK("deadlockDetector.scalability.t2.master");
|
|
moz_lock_t* locks = new moz_lock_t[N];
|
|
if (!locks)
|
|
NS_RUNTIMEABORT("couldn't allocate lock array");
|
|
|
|
for (int i = 0; i < N; ++i)
|
|
locks[i] =
|
|
NEWLOCK("deadlockDetector.scalability.t2.dep");
|
|
|
|
// establish orders
|
|
{AUTOLOCK(m, lock);
|
|
for (int i = 0; i < N; ++i)
|
|
AUTOLOCK(s, locks[i]);
|
|
}
|
|
|
|
// exercise order check
|
|
{AUTOLOCK(m, lock);
|
|
for (int i = 0; i < K; ++i)
|
|
for (int j = 0; j < N; ++j)
|
|
AUTOLOCK(s, locks[i]);
|
|
}
|
|
|
|
for (int i = 0; i < N; ++i)
|
|
DELETELOCK(locks[i]);
|
|
delete[] locks;
|
|
|
|
PASS();
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef DD_TEST3
|
|
|
|
// This test creates N resources and adds the theoretical maximum number
|
|
// of dependencies, O(N^2). It then repeats that sequence of
|
|
// acquisitions k times. Finally, all resources are freed.
|
|
//
|
|
// It's very difficult to perform well on this test. It's put forth as a
|
|
// challenge problem.
|
|
|
|
static nsresult
|
|
MaxDepsNsq(const int N, const int K)
|
|
{
|
|
moz_lock_t* locks = new moz_lock_t[N];
|
|
if (!locks)
|
|
NS_RUNTIMEABORT("couldn't allocate lock array");
|
|
|
|
for (int i = 0; i < N; ++i)
|
|
locks[i] = NEWLOCK("deadlockDetector.scalability.t3");
|
|
|
|
for (int i = 0; i < N; ++i) {
|
|
AUTOLOCK(al1, locks[i]);
|
|
for (int j = i+1; j < N; ++j)
|
|
AUTOLOCK(al2, locks[j]);
|
|
}
|
|
|
|
for (int i = 0; i < K; ++i) {
|
|
for (int j = 0; j < N; ++j) {
|
|
AUTOLOCK(al1, locks[j]);
|
|
for (int k = j+1; k < N; ++k)
|
|
AUTOLOCK(al2, locks[k]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < N; ++i)
|
|
DELETELOCK(locks[i]);
|
|
delete[] locks;
|
|
|
|
PASS();
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef DD_TEST4
|
|
|
|
// This test creates a single lock that is ordered < N resources. The
|
|
// resources are allocated, exercised K times, and deallocated one at
|
|
// a time.
|
|
|
|
static nsresult
|
|
OneLockNDepsUsedSeveralTimes(const size_t N, const size_t K)
|
|
{
|
|
// Create master lock.
|
|
moz_lock_t lock_1 = NEWLOCK("deadlockDetector.scalability.t4.master");
|
|
for (size_t n = 0; n < N; n++) {
|
|
// Create child lock.
|
|
moz_lock_t lock_2 = NEWLOCK("deadlockDetector.scalability.t4.child");
|
|
|
|
// First lock the master.
|
|
AUTOLOCK(m, lock_1);
|
|
|
|
// Now lock and unlock the child a few times.
|
|
for (size_t k = 0; k < K; k++) {
|
|
AUTOLOCK(c, lock_2);
|
|
}
|
|
|
|
// Destroy the child lock.
|
|
DELETELOCK(lock_2);
|
|
}
|
|
|
|
// Cleanup the master lock.
|
|
DELETELOCK(lock_1);
|
|
|
|
PASS();
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF(DeadlockDetectorMallocSizeOf)
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
ScopedXPCOM xpcom("Deadlock detector scalability (" __FILE__ ")");
|
|
if (xpcom.failed())
|
|
return 1;
|
|
|
|
int rv = 0;
|
|
|
|
// Uncomment these tests to run them. Not expected to be common.
|
|
|
|
#ifndef DD_TEST1
|
|
puts("Skipping not-requested LengthNDepChain() test");
|
|
#else
|
|
if (NS_FAILED(LengthNDepChain(1 << 14))) // 16K
|
|
rv = 1;
|
|
#endif
|
|
|
|
#ifndef DD_TEST2
|
|
puts("Skipping not-requested OneLockNDeps() test");
|
|
#else
|
|
// NB: Using a larger test size to stress our traversal logic.
|
|
if (NS_FAILED(OneLockNDeps(1 << 17, 100))) // 131k
|
|
rv = 1;
|
|
#endif
|
|
|
|
#ifndef DD_TEST3
|
|
puts("Skipping not-requested MaxDepsNsq() test");
|
|
#else
|
|
if (NS_FAILED(MaxDepsNsq(1 << 10, 10))) // 1k
|
|
rv = 1;
|
|
#endif
|
|
|
|
#ifndef DD_TEST4
|
|
puts("Skipping not-requested OneLockNDepsUsedSeveralTimes() test");
|
|
#else
|
|
if (NS_FAILED(OneLockNDepsUsedSeveralTimes(1 << 17, 3))) // 131k
|
|
rv = 1;
|
|
#endif
|
|
|
|
size_t memory_used = mozilla::BlockingResourceBase::SizeOfDeadlockDetector(
|
|
DeadlockDetectorMallocSizeOf);
|
|
printf_stderr("Used %d bytes\n", (int)memory_used);
|
|
|
|
return rv;
|
|
}
|