author | mikek |
Sun, 27 Jun 2010 21:43:55 +0100 | |
branch | GCC_SURGE |
changeset 181 | bd8f1e65581b |
parent 0 | a41df078684a |
permissions | -rw-r--r-- |
// 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