kernel/eka/debug/crashMonitor/src/scmdatasave.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 23 1df514389a47
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32\debug\crashMonitor\src\scmdatasave.cpp
// 
//

#define __INCLUDE_REG_OFFSETS__  // for SP_R13U in nk_plat.h

#include <omap_dbg.h>
#include "arm_mem.h"
#include "nk_plat.h"
#include <omap_assp.h>
#include <scmonitor.h>
#include <scmdatasave.h> 

/**
 * @file
 * @internal technology
 */

/**
 * SCMDataSave constructor
 * @param aMonitor - the monitor which has caught the syetem crash this object is saving data for 
 * @param aFlash - the flash memory data will be written to, note the CrashFlash interface is
 * 				   rather limited and does not support partial block writes
 * @param aFlashInfo - data describing the structure of the flash data
 */
EXPORT_C SCMDataSave::SCMDataSave(Monitor* aMonitor, CrashFlash* aFlash)
	: iMonitor(aMonitor)
		,iFlash(aFlash)
		,iByteCount(0)	
#ifdef SCM_COMM_OUTPUT	
		,iWriteSelect(EWriteComm)  // write data to debug port	
#else	
		,iWriteSelect(EWriteFlash)  // write data to flash
#endif
		,iPerformChecksum(ETrue)			 // checksum data 
		,iStartingPointForCrash(0)
	{  		
	const TInt KCacheSize = 128;
	iFlashCache = HBuf8::New(KCacheSize);
	CLTRACE1("(SCMDataSave) Creating writer with cache size = %d", KCacheSize);
	iWriter = new TCachedByteStreamWriter(const_cast<TUint8*>(iFlashCache->Ptr()), KCacheSize);
	iWriter->SetWriterImpl(this);
	}

/**
 * Destructor
 */
SCMDataSave::~SCMDataSave()
	{
	delete iFlashCache;
	}

/**
 * Getter for the current byte count. This is the amount of data that has currently 
 * been written to given media for this crash log
 * @return The number of bytes written already to given media
 */
TInt SCMDataSave::GetByteCount()
	{
	return iByteCount;
	}

/**
 * Logs the user stack for a given DThread object if it is available
 * @param aThread - thread whose stack we wish to log
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the OS codes
 */
TInt SCMDataSave::LogThreadUserStack(DThread* aThread, TBool aFullStack, TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	TUint memDumped = 0;	
	
	TUint svSp, usrSp;
	iMonitor->GetStackPointers(&(aThread->iNThread), svSp, usrSp );	
	
	//first we check for a user stack...
	if (aThread->iUserStackRunAddress && aThread->iUserStackSize)
		{		
		//Get data together
		TThreadStack usrStack;
		usrStack.iStackType = TThreadStack::EUsrStack;
		usrStack.iThreadId = (TUint64)aThread->iId;					
				
		//map in the user stack
		TUint8* usrStart = (TUint8*)iMonitor->MapAndLocateUserStack(aThread); //What about Demand paging??
		TUint8* usrEnd = (TUint8*)(usrStart + aThread->iUserStackSize);
		if(usrStart) 
			{
			TUint8* stackPointer = (TUint8*)usrSp;			
			
			//check the stack pointer is in the range of the stack...
			if (stackPointer < usrStart || stackPointer >= usrEnd)
				{
				stackPointer = usrStart;
				}
			
			//log the size of the stack we are dumping
			usrStack.iStackSize = aFullStack || (stackPointer == usrStart) ? usrEnd - usrStart : usrEnd - stackPointer;
			TUint8* dumpFrom = aFullStack ? usrStart : stackPointer;
			
			//write the stack
			aSizeDumped+= usrStack.GetSize();
			usrStack.Serialize(*iWriter);					
			
			//now we dump the actual stack
			//if there is a memErr when we read, there isnt much we can do - possibly a bit in the struct to say available/not available?
			//-1 because we dont want to write the byte at usrEnd			
			MTRAPD(memErr, LogMemory(dumpFrom, usrStack.iStackSize, aThread, memDumped));			
			if(KErrNone != memErr)
				{
				CLTRACE("Failed to log usr stack");
				}
			
			aSizeDumped+= memDumped;					
			}
		else
			{
			//write the struct
			aSizeDumped+=usrStack.GetSize();
			usrStack.Serialize(*iWriter);
			}
		}	
	return KErrNone;
	}

/**
 * Logs the supervisor stack for a given DThread object
 * @param aThread - thread whose stack we wish to log
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the system wide codes
 */
TInt SCMDataSave::LogThreadSupervisorStack(DThread* aThread, TBool aFullStack, TUint& aSizeDumped)
	{	
	LOG_CONTEXT
	aSizeDumped = 0;
	TUint memDumped;	
	
	TUint svSp, usrSp;
	iMonitor->GetStackPointers(&(aThread->iNThread), svSp, usrSp );
	
	//now we dump the supervisor stack
	TThreadStack svrStack;
	svrStack.iStackType = TThreadStack::ESvrStack;
	svrStack.iThreadId = (TUint64)aThread->iId;
	
	if (aThread->iSupervisorStack && aThread->iSupervisorStackSize)
		{
		TUint8* svrStart = (TUint8*)aThread->iSupervisorStack;
		TUint8* svrEnd = (TUint8*)(svrStart + aThread->iSupervisorStackSize);
		TUint8* svrStackPointer = (TUint8*)svSp;
		
		//size of stack we are to dump
		svrStack.iStackSize = aFullStack || (svrStackPointer == svrStart) ? svrEnd - svrStart  : svrEnd - svrStackPointer;					
		
		if(svrStart)
			{
			//check the stack pointer is in the range of the stack...
			if (svrStackPointer < svrStart || svrStackPointer >= svrEnd)
				{
				svrStackPointer = svrStart;
				}

			//write struct to flash
			aSizeDumped+=svrStack.GetSize();
			svrStack.Serialize(*iWriter);
			
			//now we dump the actual stack
			//if there is a memErr when we read, there isnt much we can do - possibly a bit in the struct to say available/not available?
			MTRAPD(memErr, LogMemory(svrStart, svrStack.iStackSize, aThread, memDumped));
			aSizeDumped+=memDumped;
			
			if(KErrNone != memErr)
				{
				CLTRACE("Failed to log supervisor stack");
				}						
			}
		else
			{
			//write the struct
			aSizeDumped+=svrStack.GetSize();
			svrStack.Serialize(*iWriter);
			}
		}
	
	return KErrNone;
	}

/**
 * Takes a DProcess kernel object and logs its corrosponding code segments
 * @param aProcess
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the OS wide error codes
 */
TInt SCMDataSave::LogCodeSegments(DProcess* aProc, TUint& aSizeDumped)
	{	
	LOG_CONTEXT
	aSizeDumped = 0;	
	
	//the code segment set for this process
	TCodeSegmentSet segSet;
	segSet.iPid = (TUint64)aProc->iId;
	
	//make sure list mutex is ok
	if(Kern::CodeSegLock()->iHoldCount)
		{
		return KErrCorrupt;
		}
	
	//get code seg list
	SDblQue queue;		
	aProc->TraverseCodeSegs(&queue, NULL, DCodeSeg::EMarkDebug, DProcess::ETraverseFlagAdd);
	
	//iterate through the list
	TInt codeSegCnt = 0;
	for(SDblQueLink* codeSegPtr= queue.First(); codeSegPtr!=(SDblQueLink*) (&queue); codeSegPtr=codeSegPtr->iNext)
		{
		//get the code seg
		DEpocCodeSeg* codeSeg = (DEpocCodeSeg*)_LOFF(codeSegPtr,DCodeSeg, iTempLink);
		
		if(codeSeg)
			{
			codeSegCnt++;
			}
		}
	
	if(codeSegCnt == 0)
		{
		return KErrNone;
		}	
	
	segSet.iNumSegs = codeSegCnt;
	segSet.Serialize(*iWriter);	
	aSizeDumped+=segSet.GetSize();
	
	TModuleMemoryInfo memoryInfo;
	
	//now we write each code segment
	for(SDblQueLink* codeSegPtr= queue.First(); codeSegPtr!=(SDblQueLink*) (&queue); codeSegPtr=codeSegPtr->iNext)
		{
		//get the code seg
		DEpocCodeSeg* codeSeg = (DEpocCodeSeg*)_LOFF(codeSegPtr,DCodeSeg, iTempLink);
		
		if(codeSeg)
			{			
			TCodeSegment seg;									
			seg.iXip = (codeSeg->iXIP) ? ETrue : EFalse;
			
			//Get the code seg type
			if(codeSeg->IsExe())
				{
				seg.iCodeSegType = EExeCodeSegType;
				}
			else if(codeSeg->IsDll())
				{
				seg.iCodeSegType = EDllCodeSegType;
				}
			
			TInt err = codeSeg->GetMemoryInfo(memoryInfo, NULL);
			if(KErrNone == err)
				{
				seg.iCodeSegMemInfo = memoryInfo;
				}
			else
				{
				seg.iCodeSegMemInfo.iCodeSize = 0; 

				// Still need to indicate it wasnt available somehow
				}
			
			//Get filename			
			seg.iNameLength = codeSeg->iFileName->Length();
			seg.iName = *(codeSeg->iFileName);
			
			aSizeDumped+=seg.GetSize();
			seg.Serialize(*iWriter);						
			}
		}
	
	//Empty this queue and clear marks
	DCodeSeg::EmptyQueue(queue, DCodeSeg::EMarkDebug);
	
	return KErrNone;
	}

/**
 * This logs the rom version and header information to the crash media
 * @param aSizeDumped amount of data occupied
 * @return one of the OS wide codes
 */
TInt SCMDataSave::LogRomInfo(TUint& aSizeDumped)	
	{
	aSizeDumped = 0;
	
	TRomHeaderData romData;
	
	TRomHeader rHdr = Epoc::RomHeader();
	
	romData.iMajorVersion = rHdr.iVersion.iMajor;
	romData.iMinorVersion = rHdr.iVersion.iMinor;
	romData.iBuildNumber = rHdr.iVersion.iBuild;
	romData.iTime = rHdr.iTime;
	
	TInt err = romData.Serialize(*iWriter);
	if(KErrNone != err)
		{
		return err;
		}
	
	aSizeDumped += romData.GetSize();
	
	return KErrNone;
	}

/**
 * Takes a DProcess kernel object and logs to flash
 * @param aProc
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the OS wide error codes
 */
TInt SCMDataSave::LogProcessData(DProcess* aProc, TUint& aSizeDumped)
	{	
	LOG_CONTEXT
	aSizeDumped = 0;	
	
	TProcessData procData;
	DCodeSeg* codeSeg = aProc->iCodeSeg;

	procData.iPriority = aProc->iPriority;
	procData.iPid = (TUint64)aProc->iId;
	
	//the code segment is not always available
	if(codeSeg)
		{
		procData.iNamesize = codeSeg->iFileName->Length();
		procData.iName = *(codeSeg->iFileName);
		}
	
	aSizeDumped += procData.GetSize();
	procData.Serialize(*iWriter);
	
	return KErrNone;
	}

/**
 * Creates meta data about the crash such as time of crash, exit reason etc. to be logged
 * later on when we have log size.
 * @param aCategory - crash category
 * @param aReason - crash reason
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the OS wide codes
 */
TInt SCMDataSave::LogCrashHeader(const TDesC8& aCategory, TInt aReason, TInt aCrashId, TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	
	//the thread that crashed is the context in which we are running
	DThread* crashedThread = &Kern::CurrentThread();
	
	iCrashInf.iPid = crashedThread->iOwningProcess->iId; 
	iCrashInf.iTid = crashedThread->iId;
	iCrashInf.iCrashTime = CrashTime();
	iCrashInf.iExitType = 0; // Not yet done: Exception or Fault - should be in category
	iCrashInf.iExitReason = aReason;
	iCrashInf.iFlashAlign = KFlashAlignment; //record the flash alignment (word aligned for now)
	iCrashInf.iCachedWriterSize = iWriter->GetCacheSize();
	
	iCrashInf.iCategorySize = aCategory.Length();
	iCrashInf.iCategory = aCategory;	
	iCrashInf.iCrashId = aCrashId;
	
	iCrashInf.iFlashBlockSize = KCrashLogBlockSize;;
	iCrashInf.iFlashPartitionSize = KCrashLogSize;;
	
	TSuperPage& sp=Kern::SuperPage();
	iCrashInf.iExcCode = sp.iKernelExcId;

	//These will be updated with more info at end of crash
	aSizeDumped+=iCrashInf.GetSize();
	iCrashInf.Serialize(*iWriter);
	
	aSizeDumped+=iHdr.GetSize();
	iHdr.Serialize(*iWriter);		

	CLTRACE1("(SCMDataSave::LogCrashHeader) finished bytes written= %d", iWriter->GetBytesWritten());
	return KErrNone;
	}

/**
 * Logs meta data about a given DThread object
 * @param aThread Thread to dump
 * @param aSizeDumped Holds the size of the data dumped
 * @return
 */
TInt SCMDataSave::LogThreadData(DThread* aThread, TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;	
	
	//struct to hold data that gets written to flash
	TThreadData threadData;
	
	threadData.iTid = (TUint64)aThread->iId;
	threadData.iOwnerId = (TUint64)aThread->iOwningProcess->iId;
	threadData.iPriority = aThread->iThreadPriority;
	
	//Get the stack pointers	
	TUint svSp, usrSp;
	iMonitor->GetStackPointers(&(aThread->iNThread), svSp, usrSp );
	threadData.iUsrSP = usrSp;
	threadData.iSvcSP = svSp;
		
	//supervisor and user stack details
	threadData.iSvcStack = (TInt32)aThread->iSupervisorStack;
	threadData.iSvcStacksize = aThread->iSupervisorStackSize;
	threadData.iUsrStack = aThread->iUserStackRunAddress;
	threadData.iUsrStacksize = aThread->iUserStackSize;	
	
	//currently we can only get the kernels heap
	if(aThread == &Kern::CurrentThread())
		{
		TInt32 heapLoc = 0;
		TInt32 heapSz = 0;
		TInt err = FindKernelHeap(heapLoc,heapSz);
		if(KErrNone == err)
			{
			threadData.iSvcHeap = heapLoc;
			threadData.iSvcHeapSize = heapSz;
			}
		else
			{
			CLTRACE("\tError: Unable to get kernel heap");
			}
		}	
	
	//get filename	
	TFileName filename;
	aThread->TraceAppendFullName(filename, EFalse);
	
	threadData.iName.Copy(filename);
	threadData.iNamesize = threadData.iName.Length();
	
		
#ifdef __INCLUDE_NTHREADBASE_DEFINES__
	threadData.iLastCpu = aThread->iNThread.iLastCpu;
#else	
	threadData.iLastCpu = aThread->iNThread.iSpare3;	
#endif	
	
	threadData.Serialize(*iWriter);
	aSizeDumped+=threadData.GetSize();
	
	return KErrNone;
	}

/**
 * Logs the arm exception stacks 
 * @param aSizeDumped Holds the size of the data dumped 
 * @return one of the OS wide codes
 */
TInt SCMDataSave::LogExceptionStacks(TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	TUint memDumped = 0;
	
	#if defined(__EPOC32__) && !defined(__CPU_X86)

	TStackInfo& stackInfo = Kern::SuperPage().iStackInfo;

	TThreadStack irqStack;
	irqStack.iStackType = TThreadStack::EIRQStack;
	irqStack.iStackSize = stackInfo.iIrqStackSize;
	
	aSizeDumped+=irqStack.GetSize();
	irqStack.Serialize(*iWriter);
	
	//now dump the IRQ memory - not much we can do in the event of an error
	MTRAPD(irqErr, LogMemory((TUint8*)stackInfo.iIrqStackBase, stackInfo.iIrqStackSize, &Kern::CurrentThread(), memDumped));	
	
	if(KErrNone != irqErr)
		{
		CLTRACE("*****Failed to log IRQ stack");
		}
	aSizeDumped+=memDumped;
	
	//Next, we do the FIQ stack
	TThreadStack fiqStack;
	fiqStack.iStackType = TThreadStack::EFIQStack;
	fiqStack.iStackSize = stackInfo.iFiqStackSize;
	
	aSizeDumped+=fiqStack.GetSize();
	fiqStack.Serialize(*iWriter);
	
	//Now dump the stack itself
	MTRAPD(fiqErr, LogMemory((TUint8*)stackInfo.iFiqStackBase, stackInfo.iFiqStackSize, &Kern::CurrentThread(), memDumped));
	
	if(KErrNone != fiqErr )
		{
		CLTRACE("*****Failed to log FIQ stack");
		}
	aSizeDumped+=memDumped;

	#endif
	
	return KErrNone;
	}

/**
 * Logs the CPU Registers at the time of crash
 * @param aSizeDumped Holds the size of the data dumped
 * @return system wide OS code
 */
TInt SCMDataSave::LogCPURegisters(TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	
	TInt32 fullSet = 37;
	
	//meta data about the thread set
	TRegisterSet threadSet;
	threadSet.iNumRegisters = fullSet;
	
	aSizeDumped+=threadSet.GetSize();
	threadSet.Serialize(*iWriter);
		
	SFullArmRegSet regSet;
	ReadCPURegisters(regSet);
	TArmReg* regs = (TArmReg*)&regSet;
		
	TInt32 cnt = 0;
	for(cnt = 0; cnt < fullSet; cnt++)
		{			
		//this is the struct to store the register value in
		TRegisterValue regVal;
		regVal.iType = cnt * 0x100;
		regVal.iValue32 = regs[cnt];
		regVal.iOwnId = Kern::CurrentThread().iId;
		
		aSizeDumped+=regVal.GetSize();
		regVal.Serialize(*iWriter);
		}

	return KErrNone;	
	}

/**
 * This logs the registers for a given thread to the flash memory
 * @param aThread - thread whose registers we want
 * @param aRegType - type of register set required such as user, supervisor etc
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the OS return codes
 */
TInt SCMDataSave::LogRegisters(DThread* aThread, const TRegisterSetType& aRegType, TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	
	TArmRegSet regs;
	TUint32 availableRegs;
	TInt err;
	
	//for the current thread we do things differently
	if(aThread == &Kern::CurrentThread() && aRegType == EFullCPURegisters)
		{
		err = LogCPURegisters(aSizeDumped);
		return err;
		} 
	else if(aThread == &Kern::CurrentThread())
		{
		//only do full cpu reg for the current thread
		return KErrNotSupported;
		}
	
	//Read the appropriate registers
	switch(aRegType)
		{
		case EUserRegisters :
			{
			err = ReadUserRegisters(aThread, regs, availableRegs);
			break;
			}
		case ESupervisorRegisters :
			{
			err = ReadSystemRegisters(aThread, regs, availableRegs);
			break;			
			}
		default : return KErrNotSupported;
		}
	
	if(err != KErrNone)
		{
		return err;
		}	
		
	//meta data about the thread set
	TRegisterSet threadSet;
	
	//to get the number of registers in advance, we need to count the number of times 1 is set in the bit field of availableRegs
	TUint numR = 0;
	for(TInt cnt =0; cnt< 8*sizeof(availableRegs); cnt++) //cycle through 1 bit at a time
		{
		if(0x1 & (availableRegs>>cnt))
			numR++;
		}
	
	threadSet.iNumRegisters = numR;
	
	if(numR == 0)
		return KErrNone;
	
	threadSet.Serialize(*iWriter);
	aSizeDumped += threadSet.GetSize();
	
	TInt32 currentRegister = 1;
	TArmReg* reg = (TArmReg*)(&regs);	
	
	for(TInt32 cnt = 0; cnt < KArmRegisterCount; cnt++)
		{		
		//look at the unavailable bitmask to see current register is available
		//only write the registers we have values for
		if(currentRegister & availableRegs)
			{
			//this is the struct to store the register value in
			TRegisterValue regVal;
						
			//get register type as per symbian elf docs
			TUint32 registerType;
			err = GetRegisterType(aRegType, cnt, registerType);
			if(err != KErrNone)
				{
				continue;
				}
			regVal.iType = registerType;
			regVal.iOwnId = aThread->iId;
			
			//set value
			regVal.iValue32 = reg[cnt];
			
			aSizeDumped+=regVal.GetSize();
			regVal.Serialize(*iWriter);
			}
		
		currentRegister<<=1; 
		}
	
	return KErrNone;
	}

/**
 * This logs memory in the specified area
 * @param aStartAddress - address to start from
 * @param aEndAddress - address to finish
 * @param aThread - process whose memory this is in
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the system wide codes
 */
TInt SCMDataSave::LogMemory(const TUint8* aStartAddress, TInt aLength, const DThread* aThread, TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;	
	
	if(aThread->iOwningProcess != &Kern::CurrentProcess())
		{
		TInt err = iMonitor->SwitchAddressSpace(aThread->iOwningProcess, ETrue);
		if(KErrNone != err)
			{
			return err;
			}
		}
	
	TMemoryDump memDump;
	memDump.iStartAddress = (TUint32)aStartAddress;
	memDump.iLength = aLength;
	memDump.iPid = aThread->iOwningProcess->iId;
	
	aSizeDumped+=memDump.GetSize();
	memDump.Serialize(*iWriter);	
	
	if(!aStartAddress)
		{
		return KErrArgument;
		}
	
	TRawData theMemory;
	theMemory.iData.Set(const_cast<TUint8*>(aStartAddress), aLength, aLength);
	
	theMemory.Serialize(*iWriter);
	aSizeDumped+=theMemory.GetSize();
	
	return KErrNone;	
	}

/**
 * This logs the locks held by system at time of crash
 * @param aSizeDumped Holds the size of the data dumped
 * @return one of the system wide codes
 */
TInt SCMDataSave::LogLocks(TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	
	// get the mutex logs & waits & log via a TLockData object		
	TSCMLockData lockData;

	const TInt KMaxLockCheck = 20; // so no possibility of infinite loop
		
	TInt lockCount = 0;
	// check for kernel locks - 	
	for(TInt i=0;i<KMaxLockCheck;i++)
		{		
		TBool locked = NKern::KernelLocked(i);	
		if(!locked)
			{
			lockData.SetLockCount(lockCount);
			break;		
			}
		// found a valid lock for value i increment the clock counter
		lockCount++;
		}
	
	// now mutexes
	DMutex* mutex = Kern::CodeSegLock();
	if(mutex)
		{
		lockData.SetMutexHoldCount(mutex->iHoldCount);
		lockData.SetMutexThreadWaitCount(mutex->iWaitCount);
		}
	else
		{
		// no mutex held set to -1
		lockData.SetMutexHoldCount(0);
		lockData.SetMutexThreadWaitCount(0);		
		}

	aSizeDumped+=lockData.GetSize();
	TInt err = lockData.Serialize(*iWriter);
	
	return err;
	}

/**
 * Writes the SCM Configuration to the start of the media
 * @param aScmConfig Configuration to write
 * @return one of the system wide codes
 */
TInt SCMDataSave::LogConfig(SCMConfiguration& aScmConfig)
	{
	iWriter->SetPosition(0);
	
	TInt err = aScmConfig.Serialize(*iWriter);
	
	if( err != KErrNone)
		{
		CLTRACE1("SCMDataSave::LogConfig failed err = %d", err);
		}

	return err;
	}

/**
 * Reads the SCM Configuration from the media
 * @param aScmConfig
 * @return one of the system wide codes
 */
TInt SCMDataSave::ReadConfig(SCMConfiguration& aScmConfig)
	{		
	const TInt KBufSize = 135; //Not yet done: Put in header, beside config defn

	if( KBufSize < aScmConfig.GetSize())
		{
		CLTRACE2("(SCMDataSave::ReadConfig) ** ERROR Inadequate buffer actual = %d req = %d"
				, KBufSize,  aScmConfig.GetSize());	
		}
	
	// try and read the configuration
	TBuf8<KBufSize> buf;
	buf.SetLength(KBufSize);
		
	iFlash->SetReadPos(0); // config always at 0
	iFlash->Read(buf);
	 
	TByteStreamReader reader(const_cast<TUint8*>(buf.Ptr()));		
	TInt err = aScmConfig.Deserialize(reader);	
	if(err == KErrNotReady)
		{
		CLTRACE("(SCMDataSave::ReadConfig) no config saved - use default");
		}	
	else if(err == KErrNone)	
		{
		CLTRACE("(SCMDataSave::ReadConfig) Config read ok"); 		
		}
	else
		{
		CLTRACE1("(SCMDataSave::ReadConfig) error reading config err = %d", err); 
		}
	
	return err;
	}

/**
 * This is a look up table to map the register type and number to the symbian elf definition 
 * of register type
 * @param aSetType this is the register set type - user, supervisor etc
 * @param aRegNumber this is the number of the register as per TArmRegisters in arm_types.h
 * @param aSizeDumped Holds the size of the data dumped
 * @return One of the OS wide codes
 */
TInt SCMDataSave::GetRegisterType(const TRegisterSetType& aSetType, TInt32& aRegNumber, TUint32& aRegisterType)
	{	
	//validate arguments
	if(aRegNumber < EArmR0 || aRegNumber > EArmFlags)
		{
		return KErrArgument;
		}
	
	//look at what type we are using
	switch(aSetType)
		{
		case EUserRegisters :
			{
			aRegisterType = aRegNumber * 0x100; //for R0 to R16 (CPSR) it just increments in 0x100 from 0x0 to 0x1000
			break;
			}
		case ESupervisorRegisters :
			{
			//same as EUserRegisters except R13 and R14 are different
			if(aRegNumber == EArmSp)
				{
				aRegisterType = 0x1100;
				break;
				}
			else if(aRegNumber == EArmLr)
				{
				aRegisterType = 0x1200;
				break;
				}
			else
				{
				aRegisterType = aRegNumber * 0x100;
				break;
				}		
			}
		default : return KErrNotSupported;
		}
	
	return KErrNone;
	}

/**
 * Writes the trace buffer to the crash log. 
 * @param aSizeToDump Number of bytes to dump. If this is zero we attempt to write the entire buffer
 * @param aSizeDumped Holds the size of the data dumped
 * @return One of the OS wide codes
 */
TInt SCMDataSave::LogTraceBuffer(TInt aSizeToDump, TUint& aSizeDumped)
	{
	LOG_CONTEXT
	aSizeDumped = 0;
	TUint memDumped = 0;
	
	TBool dumpAll = (aSizeToDump == 0) ? ETrue : EFalse;
	
	//Because the btrace buffer is a circular one, we need to save it in two parts
	//this corrosponds to how we read it	
	TUint8* data;
	TUint sizeOfPartRead;
	TInt spaceRemaining = aSizeToDump;
	
	//This structure will be filled after the first pass and cached so by the time we ARE writing it will
	//contain the data we want 
	aSizeDumped+=iTrace.GetSize();
	iTrace.Serialize(*iWriter);
	
	//read first part
	TInt err = BTrace::Control(BTrace::ECtrlCrashReadFirst,&data,&sizeOfPartRead);
	
	while(KErrNone == err && sizeOfPartRead > 0)
		{
		TUint rawSize = 0; //how much of this read data want we to dump
		
		if(dumpAll)
			{
			rawSize = sizeOfPartRead;
			}
		else	//Otherwise see what room is left for dumpage
			{
			rawSize  = ((sizeOfPartRead + iTrace.iSizeOfMemory) > aSizeToDump) ? spaceRemaining : sizeOfPartRead;
			}		
		
		//Only relevant if restricting the dump
		if(spaceRemaining <= 0 && !dumpAll)
			break;
		
		TPtrC8 ptr(data, rawSize);
		err = LogRawData(ptr, memDumped);
		if(KErrNone != err)
			{
			CLTRACE1("Logging Raw data failed - [%d]", err);
			err = BTrace::Control(BTrace::ECtrlCrashReadNext,&data,&sizeOfPartRead);
			continue;
			}
		
		aSizeDumped+=memDumped;
		
		iTrace.iSizeOfMemory += rawSize;
		iTrace.iNumberOfParts++;
		spaceRemaining -= rawSize;		
		
		err = BTrace::Control(BTrace::ECtrlCrashReadNext,&data,&sizeOfPartRead);
		}
	
	return KErrNone;
	}

/**
 * Logs the data in a TRawData struct
 * @param aData 
 * @param aSizeDumped Holds the size of the data dumped
 * @return One of the OS wide codes
 */
TInt SCMDataSave::LogRawData(const TDesC8& aData, TUint& aSizeDumped)
	{
	TRawData theData;
	theData.iLength = aData.Length();
	theData.iData.Set(const_cast<TUint8*>(aData.Ptr()), aData.Length(), aData.Length());
	
	aSizeDumped+=theData.GetSize();
	return theData.Serialize(*iWriter);	
	}


/**
 * Logs the kernels heap and returns the size dumped via aSizeDumped
 * @param aSizeDumped Holds the size of the data dumped
 * @return
 */
TInt SCMDataSave::LogKernelHeap(TUint& aSizeDumped)
	{
	LOG_CONTEXT
	
	TInt32 heapLoc = 0;
	TInt32 heapSize = 0;
	TInt32 err = FindKernelHeap(heapLoc, heapSize);
	if(KErrNone == err)
		{
		return LogMemory((TUint8*)heapLoc, heapSize, &Kern::CurrentThread(), aSizeDumped);
		}
	
	CLTRACE1("\tCouldnt find the kernel heap: [%d]", err);
	return err;
	}

/**
 * Iterates the object containers and finds the kernel heap
 * @param aHeapLocation Contains the memory location of the kernel heap
 * @param aHeapSize Contains the size of the Heap
 * @return One of the OS wide codes
 */
TInt SCMDataSave::FindKernelHeap(TInt32& aHeapLocation, TInt32& aHeapSize)
	{
	LOG_CONTEXT
	
	//Get Chunk object container
	DObjectCon* objectContainer = Kern::Containers()[EChunk];
	if(objectContainer == NULL)
		{		
		CLTRACE("\tFailed to get object container for the chunks");
		return KErrNotFound;
		}
	
	//Must check the mutex on this is ok otherwise the data will be in an inconsistent state
	if(objectContainer->Lock()->iHoldCount)
		{
		CLTRACE("\tChunk Container is in an inconsistant state");
		return KErrCorrupt;
		}
	
	TInt numObjects = objectContainer->Count();	
	
	for(TInt cnt = 0; cnt< numObjects; cnt ++)
		{		
		DChunk* candidateHeapChunk = (DChunk*)(*objectContainer)[cnt];
		
		//Get the objects name
		TBuf8<KMaxKernelName> name;
		candidateHeapChunk->TraceAppendFullName(name,EFalse);
		
		if(name == KKernelHeapChunkName)
			{
			#ifndef __MEMMODEL_FLEXIBLE__
				aHeapLocation = (TInt32)candidateHeapChunk->iBase;
			#else
				aHeapLocation = (TInt32)candidateHeapChunk->iFixedBase;
			#endif
				
				aHeapSize = candidateHeapChunk->iSize;
				
			return KErrNone;
			}
		}
	
	return KErrNotFound;
	}

/**
 * This logs the variant specific descriptor data to the crash log
 * @param aSizeDumped records how much was dumped by this function
 * @return one of the OS wide codes
 */
TInt SCMDataSave::LogVariantSpecificData(TUint& aSizeDumped)
	{
	LOG_CONTEXT
	
	aSizeDumped = 0;
	
	//Change this descriptor as required for your needs
	_LIT(KVariantSpecificData, "This is the variant specific data. Put your own here");
	
	TVariantSpecificData varData;
	varData.iSize = KVariantSpecificData().Size(); 
	
	TInt err = varData.Serialize(*iWriter);
	if(KErrNone != err)
		{
		CLTRACE1("\tLogging variant specific data failed with code [%d]", err);
		return err;
		}
	aSizeDumped+=varData.GetSize();
	
	TUint rawDataSize = 0;
	err = LogRawData(KVariantSpecificData(), rawDataSize);
	if(KErrNone != err)
		{
		CLTRACE1("\tLogging variant specific data failed with code [%d]", err);
		return err;
		}
	
	aSizeDumped+=rawDataSize;
	
	return KErrNone;
	}


/**
 * This method is the callback used by MPhysicalWriterImpl interface
 * if the TCachedByteStreamWriter is configured to use this interface
 * the callback avoids the need for temp buffers & can interface directly with the
 * flash writer methods
 * @param aData - data to write
 * @param aLen	- length of data to write
 * @param aPos  - writers internal position   
 */
void SCMDataSave::DoPhysicalWrite(TAny* aData, TInt aPos, TInt aLen)
	{	
	if(iPerformChecksum)
		{
		iChecksum.ChecksumBlock((TUint8*)aData, aLen);
		}
	
	if( this->iWriteSelect == EWriteComm)
		{	
		WriteUart((TUint8*)aData, aLen);		
		}
	else  // EWriteFlash
		{			
		Write(aData, aLen);
		}
	}

/**
 * Writes data to Flash
 * @param aSomething Pointer to the data
 * @param aSize Size of the data
 */
void SCMDataSave::Write(const TAny* aSomething, TInt aSize)
	{		
	TPtrC8 data((const TUint8 *)aSomething, aSize);
	
	TInt written = 0;
	
	WriteCrashFlash(iByteCount, written, data);
	iByteCount+= written;	
	}

/**
 * Writes a descriptor to the crash flash
 * @param aPos Position in flash to write
 * @param aSize Holds the size of the data written after the call
 * @param aBuffer Descriptor to write
 */
void SCMDataSave::WriteCrashFlash(TInt aPos, TInt& aSize, const TDesC8& aBuffer)
	{	
	//Set write position in the flash
	iFlash->SetWritePos(aPos);	
	iFlash->Write(aBuffer);
	
	//get bytes written
	aSize += iFlash->BytesWritten();
	
	if(aSize != aBuffer.Length())
		{
		CLTRACE2("(SCMDataSave::WriteCrashFlash) Over the limit aSize = %d aBuffer.Length() = %d",
				aSize,  aBuffer.Length());
		}
	}
	
/**
 * Writes a descriptor via serial
 * @param aDes Descriptor to write
 */
void SCMDataSave::WriteUart(const TDesC8& aDes)
	{
	WriteUart(aDes.Ptr(), aDes.Length());	
	}

/**
 * Writes data via serial
 * @param aData Data to write
 * @param aSize Size of data to write
 */
void SCMDataSave::WriteUart(const TUint8* aData, TInt aSize)
	{
	OMAP* assp = ((OMAP*)Arch::TheAsic());
	TOmapDbgPrt* dbg = assp->DebugPort();
		
	if (dbg)
		{
		for(TInt i=0;i<aSize;i++)
			{
			dbg->DebugOutput(*(aData+i));			
			}
		}
	else
		{
		CLTRACE("SCMDataSave::WriteUart ERROR - dbg was null");		
		}
	}

/**
 * Setter for the current number of bytes written for this crash log
 * If aByte is not word aligned, it will be rounded up to be so
 * @param aByte Current bytes written
 */
void SCMDataSave::SetByteCount(TInt aByte)
	{
	//ensure aligned
	if(aByte % iWriter->GetCacheSize() == 0)
		{
		iByteCount = aByte;
		}
	else
		{
		iByteCount = aByte + (iWriter->GetCacheSize() - (aByte % iWriter->GetCacheSize()));
		}		
	}

/**
 * Gets the output target selection
 * @return TScmWriteSelect output target selection
 * @param void
 */	
SCMDataSave::TWriteSelect SCMDataSave::GetWriteSelect()
	{
	return iWriteSelect;
	}

/**
 * Sets the output target selection
 * @return void
 * @param TScmWriteSelect aWriteSelect output target selection
 */
void SCMDataSave::SetWriteSelect(SCMDataSave::TWriteSelect aWriteSelect)
	{
	iWriteSelect = aWriteSelect;
	}

/**
 * Gets the amount of space remaining for the media of choice
 * @return
 */
TUint SCMDataSave::SpaceRemaining()
	{
	TInt currentPosition = iWriter->GetBytesWritten() + iStartingPointForCrash;
	
	return MaxLogSize() - currentPosition; 
	}

/**
 * To find the max size of a log for a given media
 * @return the max size of a log for a given media
 */
TUint SCMDataSave::MaxLogSize()
	{
	//see what write media is being used
	switch(GetWriteSelect())
		{
		case EWriteFlash:
			{
			return KMaxCrashLogSize; 
			}
		case EWriteComm:
			{
			return 0xFFFFFFFF;
			}
		default:
			{
			return 0;
			}
		} 
	}

/**
 * Records the offset in the flash partition where this crash begins
 * @param aStart Offset in flash
 */
void SCMDataSave::SetCrashStartingPoint(TUint32 aStart)
	{
	iStartingPointForCrash = aStart;
	}

//eof