gecko-dev/lib/mac/MacMemoryAllocator/test/CMemoryWorkout.cp

417 строки
12 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
//----------------------------------------------------------------------------------------
/*
A set of classes for giving the memory allocators a thorough workout.
CMemoryBlock
This class manages a single memory block, which is allocated with
malloc(). The block is filled with a patter, and tag words put at
each end. The various operators allow you to change the block size;
for each change, the block is realloc'd and the tag words replaced.
Before each operation, the block contents are checked to ensure that
no-one wrote over them.
CMemoryTester
This class manages an array of CMemoryBlocks. In DoMemoryTesting(), it
allocates about 200 block with random sizes in a wide range, then
makes them bigger, smaller, frees some, allocates some more, and resizes
them again.
Its destructor frees the blocks.
CMemoryTesterPeriodical
This is a periodical that runs the memory tests. In the current implementaion,
it manages an array of CMemoryTesters. On each idle, it allocates a new
CMemoryTester, and does the testing. That CMemoryTester is put in an
array to keep track of it (but the memory is not freed yet). So on every
idle it "leaks" all the blocks managed by the tester. Memory soon gets
used up.
At some point an allocation will fail, and CMemoryBlock throws an exception.
This is caught by the CMemoryTesterPeriodical, which then frees up all the
CMemoryTesters. Next time around, it all starts again.
How to use:
Hook up your periodical in a convenient place like CFrontApp::Initialize():
CMemoryTesterPeriodical *tester = new CMemoryTesterPeriodical();
tester->StartIdling();
The app starts to run real slow, and if you watch memory use in the Finder,
you will see it fluctuate wildly as the periodical leaks lots of memory,
frees it all, then leaks it again. Since this is running on a periodical,
you can be browsing the web at the same time, albeit slowly.
If any errors are detected (e.g. the block tags have been overwritten),
then you will assert.
*/
//----------------------------------------------------------------------------------------
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <LArrayIterator.h>
#include "CMemoryWorkout.h"
#define kMinSize 8
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
//----------------------------------------------------------------------------------------
CMemoryBlock::CMemoryBlock()
: mBlockData(NULL)
, mBlockSize(0)
//----------------------------------------------------------------------------------------
{
}
//----------------------------------------------------------------------------------------
CMemoryBlock::CMemoryBlock(UInt32 inBlockSize)
: mBlockSize(inBlockSize)
//----------------------------------------------------------------------------------------
{
if (mBlockSize < kMinSize)
mBlockSize = kMinSize;
mBlockData = (char *)malloc(mBlockSize);
ThrowIfNil_(mBlockData);
InsertCheckBlocks();
}
//----------------------------------------------------------------------------------------
CMemoryBlock::~CMemoryBlock()
//----------------------------------------------------------------------------------------
{
if (GetBlockData())
{
Boolean isGood = QuickCheckBlock();
Assert_(isGood);
}
free(mBlockData);
}
//----------------------------------------------------------------------------------------
long *CMemoryBlock::GetBlockStartPtr()
//----------------------------------------------------------------------------------------
{
return (long *)mBlockData;
}
//----------------------------------------------------------------------------------------
long *CMemoryBlock::GetBlockEndPtr()
//----------------------------------------------------------------------------------------
{
if (!mBlockData) return nil;
char *dataEnd = mBlockData + mBlockSize;
return (long *)(dataEnd - sizeof(long));
}
//----------------------------------------------------------------------------------------
Boolean CMemoryBlock::QuickCheckBlock()
//----------------------------------------------------------------------------------------
{
ThrowIfNil_(mBlockData);
long *startCheckPtr = GetBlockStartPtr();
long *endCheckPtr = GetBlockEndPtr();
return (*startCheckPtr == eBlockStartMarker && *endCheckPtr == eBlockEndMarker);
}
//----------------------------------------------------------------------------------------
Boolean CMemoryBlock::ThoroughCheckBlock()
//----------------------------------------------------------------------------------------
{
if (!QuickCheckBlock())
return false;
return true; // I'm too lazy to do the whole thing
}
//----------------------------------------------------------------------------------------
void CMemoryBlock::InsertCheckBlocks()
//----------------------------------------------------------------------------------------
{
ThrowIfNil_(mBlockData);
memset(mBlockData, eBlockFillPattern, mBlockSize);
long *startCheckPtr = GetBlockStartPtr();
long *endCheckPtr = GetBlockEndPtr();
*startCheckPtr = eBlockStartMarker;
*endCheckPtr = eBlockEndMarker;
}
//----------------------------------------------------------------------------------------
void CMemoryBlock::ChangeBlockSize(UInt32 newSize)
//----------------------------------------------------------------------------------------
{
ThrowIfNil_(mBlockData);
Assert_(QuickCheckBlock());
mBlockSize = MAX(newSize, kMinSize);
mBlockData = (char *)realloc((void *)mBlockData, mBlockSize);
ThrowIfNil_(mBlockData);
InsertCheckBlocks();
}
//----------------------------------------------------------------------------------------
void CMemoryBlock::operator++(int)
//----------------------------------------------------------------------------------------
{
ChangeBlockSize(mBlockSize + 1);
}
//----------------------------------------------------------------------------------------
void CMemoryBlock::operator--(int)
//----------------------------------------------------------------------------------------
{
ChangeBlockSize(mBlockSize - 1);
}
//----------------------------------------------------------------------------------------
void CMemoryBlock::operator+=(UInt32 incSize)
//----------------------------------------------------------------------------------------
{
ChangeBlockSize(mBlockSize + incSize);
}
//----------------------------------------------------------------------------------------
void CMemoryBlock::operator-=(UInt32 decSize)
//----------------------------------------------------------------------------------------
{
if (decSize > mBlockSize)
decSize = 0;
UInt32 newSize = mBlockSize - decSize;
ChangeBlockSize(newSize > kMinSize ? newSize : kMinSize);
}
#pragma mark -
//----------------------------------------------------------------------------------------
CMemoryTester::CMemoryTester()
: mBlocksArray(sizeof(CMemoryBlock *))
//----------------------------------------------------------------------------------------
{
}
//----------------------------------------------------------------------------------------
CMemoryTester::~CMemoryTester()
//----------------------------------------------------------------------------------------
{
LArrayIterator iter(mBlocksArray);
CMemoryBlock *theBlock;
while (iter.Next(&theBlock) && theBlock)
{
delete theBlock;
}
mBlocksArray.RemoveItemsAt(mBlocksArray.GetCount(), LArray::index_First);
}
//----------------------------------------------------------------------------------------
void CMemoryTester::DoMemoryTesting()
//----------------------------------------------------------------------------------------
{
// let's make some objects with random sizes
for (Int32 i = 0; i < 10; i ++)
{
CMemoryBlock *newBlock = new CMemoryBlock( 1024 * (::Random() & 0x05FF) + (::Random() & 0x7FFF));
mBlocksArray.InsertItemsAt(1, LArray::index_Last, &newBlock);
}
for (Int32 i = 0; i < 100; i ++)
{
CMemoryBlock *newBlock = new CMemoryBlock( 1024 * (::Random() & 0x07F) + (::Random() & 0x7FFF));
mBlocksArray.InsertItemsAt(1, LArray::index_Last, &newBlock);
}
for (Int32 i = 0; i < 3000; i ++)
{
CMemoryBlock *newBlock = new CMemoryBlock(::Random() & 0x009F);
mBlocksArray.InsertItemsAt(1, LArray::index_Last, &newBlock);
}
/* now let's resize them
LArrayIterator iter(mBlocksArray);
CMemoryBlock *theBlock;
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock)++;
}
iter.ResetTo(LArrayIterator::index_BeforeStart);
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock)++;
}
iter.ResetTo(LArrayIterator::index_BeforeStart);
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock)++;
}
iter.ResetTo(LArrayIterator::index_BeforeStart);
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock) -= 10;
}
iter.ResetTo(LArrayIterator::index_BeforeStart);
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock) += (::Random() & 0x07FF);
}
iter.ResetTo(LArrayIterator::index_BeforeStart);
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock) += 0; // resize to same size
}
// now let's free every other block, and then reallocate them
iter.ResetTo(LArrayIterator::index_BeforeStart);
ArrayIndexT n = LArray::index_First;
while (iter.Next(&theBlock) && theBlock)
{
if ((n & 1) == 0)
{
delete theBlock;
theBlock = new CMemoryBlock(::Random() & 0x09F);
mBlocksArray.AssignItemsAt(1, n, &theBlock);
}
n++;
}
// now let's free every other block, and then reallocate them
iter.ResetTo(LArrayIterator::index_BeforeStart);
n = LArray::index_First;
while (iter.Next(&theBlock) && theBlock)
{
if ((n & 1) == 1)
{
delete theBlock;
theBlock = new CMemoryBlock(::Random() & 0x09F);
mBlocksArray.AssignItemsAt(1, n, &theBlock);
}
n++;
}
*/
/*
iter.ResetTo(LArrayIterator::index_BeforeStart);
while (iter.Next(&theBlock) && theBlock)
{
(*theBlock) -= (::Random() & 0x07FF);
}
*/
}
//----------------------------------------------------------------------------------------
CMemoryTesterPeriodical::CMemoryTesterPeriodical()
: LPeriodical()
, mTestersArray(sizeof(CMemoryTester *))
//----------------------------------------------------------------------------------------
{
}
//----------------------------------------------------------------------------------------
CMemoryTesterPeriodical::~CMemoryTesterPeriodical()
//----------------------------------------------------------------------------------------
{
FreeTesters();
}
//----------------------------------------------------------------------------------------
void CMemoryTesterPeriodical::FreeTesters()
//----------------------------------------------------------------------------------------
{
LArrayIterator iter(mTestersArray);
CMemoryTester *theTester;
while (iter.Next(&theTester) && theTester)
{
delete theTester;
}
mTestersArray.RemoveItemsAt(mTestersArray.GetCount(), LArray::index_First);
}
//----------------------------------------------------------------------------------------
void CMemoryTesterPeriodical::SpendTime(const EventRecord & /*inMacEvent */)
//----------------------------------------------------------------------------------------
{
try
{
CMemoryTester *tester = new CMemoryTester();
mTestersArray.InsertItemsAt(1, LArray::index_Last, &tester);
tester->DoMemoryTesting();
// leak the damn thing
}
catch (...)
{
DebugStr("\pWe ran out of memory. Free up time.");
FreeTesters();
}
}