diff -r 000000000000 -r 96e5fb8b040d kernel/eka/debug/crashMonitor/src/crashlogwalker.cpp --- /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 +#include +#include +#include +#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(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(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 +