commands/leak/leak.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 37 534b01198c2d
parent 31 d0e1c40de386
permissions -rw-r--r--
Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase. Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.

// leak.cpp
// 
// Copyright (c) 2007 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
// 
// Initial Contributors:
// Accenture - Initial contribution
//

#include "leak.h"
#include <fshell/common.mmh>
#ifdef FSHELL_QR3_SUPPORT_LOGGINGALLOCATOR
#include <fshell/loggingallocator.h>
#endif

const TInt KMinChunkSize = 4 * 1024;
const TInt KMaxChunkSize = 512 * 1024 * 1024;

CCommandBase* CCmdLeak::NewLC()
	{
	CCmdLeak* self = new(ELeave) CCmdLeak();
	CleanupStack::PushL(self);
	self->BaseConstructL();
	return self;
	}

CCmdLeak::~CCmdLeak()
	{
#ifdef FSHELL_QR3_SUPPORT_LOGGINGALLOCATOR
	if (iLoggingAllocator)
		{
		// Hmm how do I clean up a logging allocator created with RLoggingAllocator::New()?
		}
#endif
	iChunk.Close();
	if (iChunkHeap)
		{
		iChunkHeap->Reset();
		iChunkHeap->Close();
		}
	}

CCmdLeak::CCmdLeak()
	{
	}

const TDesC& CCmdLeak::Name() const
	{
	_LIT(KName, "leak");
	return KName;
	}

void CCmdLeak::DoRunL()
	{
	if ((iAmount < 0) || (iIncrementAmount < 0))
		{
		User::Leave(KErrArgument);
		}

	if (iArguments.IsPresent(0))
		{
		if (iIncrementAmount == 0)
			{
			iIncrementAmount = iAmount;
			}
		}
	else
		{
		iAmount = KMaxTInt;
		if (iIncrementAmount == 0)
			{
			iIncrementAmount = KMinChunkSize;
			}
		}

	if (iUseLoggingAllocator) iUseHeap = ETrue; // Using the logging allocator implies the --heap option

	if (iUseHeap)
		{
		iChunkHeap = UserHeap::ChunkHeap(NULL, KMinChunkSize, 256*1024*1024);
		if (!iChunkHeap) LeaveIfErr(KErrNoMemory, _L("Couldn't create chunk heap"));
		iAllocatorToUse = iChunkHeap;

#ifdef FSHELL_QR3_SUPPORT_LOGGINGALLOCATOR
		if (iUseLoggingAllocator)
			{
			LeaveIfErr(RLoggingAllocator::New(0, iChunkHeap, iLoggingAllocator), _L("Couldn't create logging allocator"));
			iAllocatorToUse = iLoggingAllocator;
			}
#endif
		}
	else
		{
		if (iIncrementAmount & 4095) LeaveIfErr(KErrArgument, _L("increment-amount must be a multiple of 4096"));
		TInt err = iChunk.CreateLocal(KMinChunkSize, KMaxChunkSize);
		LeaveIfErr(err, _L("Failed to create local chunk"));
		}

	RIoConsoleWriteHandle stdout = Stdout();
	stdout.SetCursorHeight(0);
	TInt err = KErrNone;
	do
		{
		err = LeakStep(Min(iIncrementAmount, iAmount - iCurrentLeak));
		if (iVerbose)
			{
			Write(_L("\r"));
			stdout.ClearToEndOfLine();
			Printf(_L("Allocated %d: %d"), iCurrentLeak, err);
			}
		if (err && iRetry && iIncrementAmount > 4)
			{
			// Keep going with a smaller increment
			err = KErrNone;
			iIncrementAmount /= 2;
			continue;
			}

		if (iRate > 0)
			{
			User::After(iRate * 1000);
			}
		}
		while ((err == KErrNone) && (iCurrentLeak < iAmount));

	if (err == KErrNoMemory)
		{
		Printf(_L("Out of memory. "));
		}
	else if (err)
		{
		Printf(_L("Error: %d. "), err);
		}

	Printf(_L("\r\nLeaked %d bytes. Press any key to exit (and free the memory).\r\n"), iCurrentLeak);

	RIoConsoleReadHandle stdin = Stdin();
	stdin.ReadKey();
	}

void CCmdLeak::OptionsL(RCommandOptionList& aOptions)
	{
	_LIT(KOptVerbose, "verbose");
	aOptions.AppendBoolL(iVerbose, KOptVerbose);

	_LIT(KOptIncrementAmount, "increment-amount");
	aOptions.AppendIntL(iIncrementAmount, KOptIncrementAmount);

	_LIT(KOptRate, "rate");
	aOptions.AppendUintL(iRate, KOptRate);

	_LIT(KOptUseHeap, "heap");
	aOptions.AppendBoolL(iUseHeap, KOptUseHeap);

	_LIT(KOptRetry, "retry");
	aOptions.AppendBoolL(iRetry, KOptRetry);

#ifdef FSHELL_QR3_SUPPORT_LOGGINGALLOCATOR
	_LIT(KOptUseLoggingAllocator, "logging-allocator");
	aOptions.AppendBoolL(iUseLoggingAllocator, KOptUseLoggingAllocator);
#endif
	}

void CCmdLeak::ArgumentsL(RCommandArgumentList& aArguments)
	{
	_LIT(KArgAmount, "amount");
	aArguments.AppendIntL(iAmount, KArgAmount);
	}


#ifdef EXE_BUILD
EXE_BOILER_PLATE(CCmdLeak)
#endif

TInt CCmdLeak::LeakStep(TInt aAmount)
	{
	TInt err = KErrNone;
	if (iAllocatorToUse)
		{
		TAny* cell = iAllocatorToUse->Alloc(aAmount);
		if (!cell) err = KErrNoMemory;
		}
	else
		{
		if (aAmount < 4096) err = KErrNoMemory; // implies we're retrying - unlike the others that keep retrying down to 4 bytes, we have to stop at page granularity
		else err = iChunk.Adjust(iCurrentLeak + aAmount);
		}

	if (!err) iCurrentLeak += aAmount;
	return err;
	}