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