// 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