kernel/eka/debug/crashMonitor/src/scmdatasave.cpp
changeset 0 a41df078684a
child 23 1df514389a47
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/debug/crashMonitor/src/scmdatasave.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1240 @@
+// 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
+