kernel/eka/debug/crashMonitor/src/crashlogwalker.cpp
changeset 43 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/debug/crashMonitor/src/crashlogwalker.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,535 @@
+// 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\crashlogwalker.cpp
+// Class to allow us to traverse the crash log generated by System Crash Monitor
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#ifndef __KERNEL_MODE__
+#include <e32std.h>
+#include <e32std_private.h> 
+#include <e32base.h>
+#include <e32base_private.h> 
+#endif
+
+#include "scmtrace.h"
+#include "crashlogwalker.h"
+
+namespace Debug
+	{	
+	/**
+	 * Constructor for log walker
+	 * @param aBuffer The buffer containing the crash data
+	 */
+	TCrashLogWalker::TCrashLogWalker(TDesC8& aBuffer) : 
+		iBuffer(aBuffer),
+		iReader(const_cast<TUint8*>(iBuffer.Ptr()))
+		{
+		}
+	
+	/**
+	 * This reads in the crash header from the buffer from the given start point
+	 * @param aStartPoint Point to begin reading in buffer
+	 * @return One of the OS wide codes
+	 */
+	TInt TCrashLogWalker::ReadLogHeader(const TInt aStartPoint)
+		{		
+		iReader.SetPosition(aStartPoint);
+		
+		TInt err = iCrashHeader.Deserialize(iReader);
+		if(err != KErrNone)
+			{
+			CLTRACE("(TCrashLogWalker::ReadLogHeader) - failed to read crash header");
+			return KErrCorrupt;
+			}
+		
+		err = iOffsets.Deserialize(iReader);
+		if(err != KErrNone)
+			{
+			CLTRACE("(TCrashLogWalker::ReadLogHeader) - failed to read offsets");
+			return KErrCorrupt;
+			}
+		
+		TRegisterSet set;
+		err = set.Deserialize(iReader);
+		if(err != KErrNone)
+			{
+			CLTRACE("(TCrashLogWalker::ReadLogHeader) - failed to read register set");
+			return KErrCorrupt;
+			}
+		
+		for(TInt cnt = 0; cnt < set.iNumRegisters; cnt++)
+			{
+			TRegisterValue val;
+			err = val.Deserialize(iReader);
+			if(err != KErrNone)
+				{
+				CLTRACE1("(TCrashLogWalker::ReadLogHeader) - failed to read TRegisterValue cnt = %d", cnt);
+				return KErrCorrupt;
+				}
+						
+			HelpAssignRegisterToContext(val);
+			}
+		
+		return VerifyHeader();
+		}
+	
+	/**
+	 * Getter for the crash context - This is the CPU register set at the time of crash
+	 * @return Crash Context
+	 */
+	const TRmdArmExcInfo& TCrashLogWalker::GetCrashContext() const
+		{
+		return iContext;
+		}
+	
+	/**
+	 * Returns the crash size for the crash that has just been read, provided the
+	 * reading of the header was succesful. 
+	 * @see ReadLogHeader
+	 * @return Crash Log size
+	 */
+	TInt TCrashLogWalker::GetCrashSize() const
+		{
+		return iCrashHeader.iLogSize;
+		}
+	
+	/**
+	 * Returns the crash ID for the crash that has just been read, provided the
+	 * reading of the header was succesful. 
+	 * @see ReadLogHeader
+	 * @return Crash Log ID
+	 */
+	TInt TCrashLogWalker::GetCrashId() const
+		{
+		return iCrashHeader.iCrashId;
+		}
+	
+	/**
+	 * Looks at the member crash log header and checks that it is valid.
+	 * @see ReadLogHeader 
+	 * @return one of the OS wide codes
+	 */
+	TInt TCrashLogWalker::VerifyHeader()
+		{
+		if(iCrashHeader.iId == ESCMTCrashInfo)
+			{			
+			CLTRACE("TCrashLogWalker::VerifyHeader() OK");
+			return KErrNone;
+			}
+		else
+			{
+			CLTRACE("TCrashLogWalker::VerifyHeader() FAILED");
+			return KErrCorrupt;
+			}
+		}
+	
+	/**
+	 * Updates the buffer being used by the crash walker and resets reader to use 
+	 * the beginning of this
+	 * @param aBuffer New buffer
+	 */
+	void TCrashLogWalker::UpdateBuffer(TDesC8& aBuffer)
+		{
+		iBuffer = aBuffer;		
+		
+		//Read from start of this buffer		
+		iReader = TByteStreamReader(const_cast<TUint8*>(aBuffer.Ptr()));
+		}
+	
+#ifndef __KERNEL_MODE__
+	/**
+	 * Gets the next data type from the buffer. If this is NULL it means the buffer was too small.
+	 * Call again with a larger buffer. It assumes the buffer contains valid data and leaves with KErrCorrupt 
+	 * if it isnt. Note for raw data types, the data will be empty. This should be read with GetRawDataTypeL after reseting the reader to the 
+	 * correct position. If you just want to skip a raw data type, move the buffer along the size of (contained in the returned struct)
+	 * 
+	 * @see GetRawDataTypeL
+	 * @see UpdateBuffer
+	 * @param aPos Next position that will be read. If we return NULL, this is the position the next buffer should
+	 * 			begin from
+	 * @param aId ID of the MByteStreamSerializable returned	 
+	 * @param buffer size to be used the next time. Unchanged if the current buffer is ok
+	 * @return MByteStreamSerializable pointer. Ownership is passed to caller. NULL if failed
+	 * @leave KErrCorrupt if the buffer cant be read
+	 */
+	MByteStreamSerializable* TCrashLogWalker::GetNextDataTypeL(TInt& aPos, SCMStructId& aId, TInt& aBufferSize)
+		{
+		MByteStreamSerializable* data = NULL;
+		
+		TInt roomInBuffer = iBuffer.Length() - iReader.CurrentPosition();
+		//make sure we have at LEAST 4 bytes in the buffer
+		if(roomInBuffer < (TInt)(sizeof(TInt)))
+			{
+			aBufferSize = sizeof(TInt);
+			return NULL;
+			}
+		
+		//this stores the required size in which to deserialize a structure - to make sure 
+		//there is room in the buffer
+		TInt maxSize = 0;
+		aPos = iReader.CurrentPosition();
+		aBufferSize = iBuffer.Length();		
+		
+		//all these data types are defined by their first byte
+		aId = (SCMStructId)iBuffer.Ptr()[iReader.CurrentPosition()];		
+		
+		//ensure we have a valid structure found
+		if(aId <= 0 || aId >= ESCMLast)
+			{
+			//oddness is afoot and the mist of corruption reigns thick
+			User::Leave(KErrCorrupt);
+			}					
+		
+		switch(aId)
+			{
+			case ESCMOffsetsHeader:
+				{
+				data = new TCrashOffsetsHeader();	
+				maxSize = TCrashOffsetsHeader::KSCMCrashOffsetsMaxSize;
+				break;
+				}
+			case ESCMTCrashInfo:
+				{
+				data = new TCrashInfoHeader();
+				maxSize = TCrashInfoHeader::KSCMCrashInfoMaxSize;
+				break;
+				}
+			case ESCMProcessData:
+				{
+				data = new TProcessData();
+				maxSize = TProcessData::KSCMProcessDataMaxSize;
+				break;
+				}
+			case ESCMThreadData:
+				{
+				data = new TThreadData();
+				maxSize = TThreadData::KSCMThreadDataMaxSize;
+				break;
+				}
+			case ESCMThreadStack:
+				{
+				data = new TThreadStack();
+				maxSize = TThreadStack::KSCMThreadStackMaxSize;
+				break;
+				}
+			case ESCMRegisterValue:
+				{
+				data = new TRegisterValue();
+				maxSize = TRegisterValue::KSCMRegisterValueMaxSize;
+				break;
+				}
+			case ESCMRegisterSet:
+				{
+				data = new TRegisterSet();
+				maxSize = TRegisterSet::KSCMRegisterSetMaxSize;
+				break;
+				}
+			case ESCMMemory:
+				{
+				data = new TMemoryDump();
+				maxSize = TMemoryDump::KSCMMemDumpMaxSize;
+				break;
+				}
+			case ESCMCodeSegSet:
+				{
+				data = new TCodeSegmentSet();
+				maxSize = TCodeSegmentSet::KSCMCodeSegSetMaxSize;
+				break;
+				}
+			case ESCMCodeSeg:
+				{
+				data = new TCodeSegment();
+				maxSize = TCodeSegment::KMaxSegmentNameSize;
+				break;
+				}	
+			case ESCMLocks:
+				{
+				data = new TSCMLockData();
+				maxSize = TSCMLockData::KSCMLockDataMaxSize;	
+				break;
+				}
+			case ESCMVariantData:
+				{
+				data = new TVariantSpecificData();
+				maxSize = TVariantSpecificData::KSCMVarSpecMaxSize;	
+				break;
+				}				
+			case ESCMRomHeader:
+				{
+				data = new TRomHeaderData();
+				maxSize = TRomHeaderData::KSCMRomHdrMaxSize;	
+				break;
+				}				
+			case ESCMRawData:
+				{
+				data = new TRawData();
+				
+				//This is a special case. The data in here can be any length, so we need to deserialise it
+				//to find this length out. The MAX_SIZE of this class is the max size minus data
+				//which is fine if we dont assign the TPtr8.				
+				if(TRawData::KSCMRawDataMaxSize > roomInBuffer )
+					{					
+					aBufferSize = (maxSize > aBufferSize) ? maxSize : aBufferSize;
+					
+					if(data)
+						delete data;
+					
+					return NULL;
+					}
+				else
+					{
+					data->Deserialize(iReader);
+					maxSize = data->GetSize();
+					
+					aPos = iReader.CurrentPosition();
+					return data;
+					}
+				}
+			case ESCMTraceData:
+				{
+				data = new TTraceDump();
+				maxSize = TTraceDump::KSCMTraceDumpMaxSize;
+				break;
+				}
+			default :
+				{
+				User::Panic(_L("Unexpected Null. Unrecognised Data type from crash log."), KErrGeneral); //Programming error				
+				}							
+			}
+		
+		__ASSERT_ALWAYS((data != NULL), User::Panic(_L("Unexpected Null"), KErrGeneral));
+		
+		if(maxSize > roomInBuffer )
+			{
+			//Not enough room in buffer to read this. Tell caller where in the current buffer
+			//we were via aPos, and what minimum size buffer should be used next time to allow reading
+			aBufferSize = maxSize;
+			if(data)
+				{
+				delete data;
+				}
+			
+			return NULL;
+			}
+		
+		if(!data)
+			{
+			CLTRACE("Unable to create data structure");
+			User::Leave(KErrAbort);
+			}
+		
+		data->Deserialize(iReader);			
+		aPos = iReader.CurrentPosition();
+		
+		return data;
+		}
+	
+	/**
+	 * Assuming the next type in the buffer is a TRawData type, this will return the TRawData
+	 * types data pointer pointing to the buffer passed via aRawBuf.
+	 *  
+	 * The difference between this call and GetNextDataTypeL is that GetNextDataTypeL only lets you move along the buffer
+	 * it doesnt allow you to specify a buffer in which to store the raw data.
+	 * 
+	 * @see GetNextDataTypeL
+	 * @return TRawData* This is the TRawData object that holds the data via the buffer passed in. Ownership is passed to the caller
+	 * @param aPos position in buffer its been found. If we return NULL, this is the position the next buffer should
+	 * 			begin from
+	 * @param aBufferSize Should we return NULL, that means the descriptor passed in was not big enough and should be of at least aBufferSize bytes
+	 * @param aRawBuf The buffer to store the data refered to by the TRawData returned
+	 * @param aStartRawPosition The point in the raw data at which we will start to put it into the buffer
+	 * @leave One of the OS wide codes
+	 */
+	TRawData* TCrashLogWalker::GetRawDataTypeL(TInt& aPos, TInt& aBufferSize, TDes8& aRawBuf, TInt aStartRawPosition)
+		{								
+		//make sure we have at LEAST the size of the struct in the buffer
+		if(iBuffer.Length() < TRawData::KSCMRawDataMaxSize)
+			{
+			aBufferSize = TRawData::KSCMRawDataMaxSize;
+			return NULL;
+			}
+		
+		//this stores the required size in which to deserialize a structure - to make sure 
+		//there is room in the buffer
+		aPos = iReader.CurrentPosition();
+		aBufferSize = iBuffer.Length();
+		
+		//all these data types are defined by their first byte
+		TInt id = (SCMStructId)iBuffer.Ptr()[iReader.CurrentPosition()];
+		if(id != ESCMRawData)
+			{
+			User::Leave(KErrCorrupt);
+			}		
+		
+		//Deserialise once to get the length (this will ignore the data in the absence of a Tptr)
+		TRawData* data = new TRawData();
+		data->Deserialize(iReader);
+		
+		//reset reader to where the raw data starts again
+		iReader.SetPosition(aPos);
+		
+		//now we know we have room, deserialize into this descriptor	
+		aRawBuf.SetMax();	
+		data->iData.Set(aRawBuf.MidTPtr(0));
+		User::LeaveIfError(data->Deserialize(aStartRawPosition, iReader));
+		
+		return data;
+		}
+	
+#endif
+	
+	/**
+	 * This is a helper function to convert between the two formats of register
+	 * @param aRegVal Resulting register values
+	 */
+	void TCrashLogWalker::HelpAssignRegisterToContext(const TRegisterValue& aRegVal)
+		{
+		//only interested in core registers at the moment
+		if(aRegVal.iClass != 0 || aRegVal.iSize != 2)
+			{
+			return;
+			}
+		
+		//Is there a cleverer way to do this with bitmasks and FOFF ?
+		switch(aRegVal.iType)
+			{
+			case 0x0 :
+				{
+				iContext.iR0 = aRegVal.iValue32;
+				break;
+				}
+			case 0x100 :
+				{
+				iContext.iR1 = aRegVal.iValue32;
+				break;
+				}
+			case 0x200 :
+				{
+				iContext.iR2 = aRegVal.iValue32;
+				break;
+				}
+			case 0x300 :
+				{
+				iContext.iR3 = aRegVal.iValue32;
+				break;
+				}
+			case 0x400 :
+				{
+				iContext.iR4 = aRegVal.iValue32;
+				break;
+				}
+			case 0x500 :
+				{
+				iContext.iR5 = aRegVal.iValue32;
+				break;
+				}
+			case 0x600 :
+				{
+				iContext.iR6 = aRegVal.iValue32;
+				break;
+				}
+			case 0x700 :
+				{
+				iContext.iR7 = aRegVal.iValue32;
+				break;
+				}
+			case 0x800 :
+				{
+				iContext.iR8 = aRegVal.iValue32;
+				break;
+				}
+			case 0x900 :
+				{
+				iContext.iR9 = aRegVal.iValue32;
+				break;
+				}
+			case 0xa00 :
+				{
+				iContext.iR10 = aRegVal.iValue32;
+				break;
+				}
+			case 0xb00 :
+				{
+				iContext.iR11 = aRegVal.iValue32;
+				break;
+				}
+			case 0xc00 :
+				{
+				iContext.iR12 = aRegVal.iValue32;
+				break;
+				}
+			case 0xd00 :
+				{
+				iContext.iR13 = aRegVal.iValue32;				
+				break;
+				}
+			case 0xe00 :
+				{
+				iContext.iR14 = aRegVal.iValue32;
+				break;
+				}
+			case 0xf00 :
+				{
+				iContext.iR15 = aRegVal.iValue32;
+				break;
+				}
+			case 0x1000 :
+				{
+				iContext.iCpsr = aRegVal.iValue32;
+				break;
+				}
+			case 0x1100 :
+				{
+				iContext.iR13Svc = aRegVal.iValue32;
+				break;
+				}
+			case 0x1200 :
+				{
+				iContext.iR14Svc = aRegVal.iValue32;
+				break;
+				}
+			default :
+				{
+				return;
+				}				
+			}
+		}
+	
+	/**
+	 * Getter for crash header
+	 * @return header
+	 */
+	const TCrashInfoHeader& TCrashLogWalker::GetCrashHeader() const
+		{
+		return iCrashHeader;
+		}
+	
+	/**
+	 * Getter for crash offsets header
+	 * @return header
+	 */
+	const TCrashOffsetsHeader& TCrashLogWalker::GetOffsetsHeader() const
+		{
+		return iOffsets;
+		}			
+		
+	}
+//eof
+