gecko/xpcom/tests/TestDeadlockDetectorScalability.cpp
Mike Hommey 6dd000340e Bug 1097507 - Make libxul independent of libdmd when DMD is enabled. r=njn
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.
2014-11-18 19:21:06 +09:00

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;
}