diff -r a151135b0cf9 -r aa2539c91954 tracesrv/tracecore/btrace_handler/test/tracedataparser/src/tracedataparser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tracesrv/tracecore/btrace_handler/test/tracedataparser/src/tracedataparser.cpp Fri Oct 08 14:56:39 2010 +0300 @@ -0,0 +1,2403 @@ +// Copyright (c) 2005-2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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: +// Trace Data parser +// + +#include +#include +#include +#include + +#include "tracedataparser.h" +#include "testdatawriternotifier.h" +#include "tracecoreconstants.h" +#include "d32tracebuffer.h" + + +EXPORT_C TUint32 TTraceDataParser::Swap(TUint32 x) + { + return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24); + } + +EXPORT_C TUint32 TTraceDataParser::ReadUint32FromBuf(TUint8*& aBuf, TBool aFromTestWriter) + { +#ifdef __WINS__ + aFromTestWriter = EFalse; +#endif + // reads a 4 byte integer of expected endianess + TUint32 n; + if (!aFromTestWriter) + { + n = *(aBuf+3) + (*(aBuf+2) << 8) + (*(aBuf+1) << 16) + (*aBuf << 24); + } + else + { + // endianess order is reversed for TestWriter on hw + n = (*aBuf) + (*(aBuf+1) << 8) + (*(aBuf+2) << 16) + (*(aBuf+3) << 24); + } + aBuf += sizeof(TUint32); + return n; + } + +EXPORT_C TUint16 TTraceDataParser::ReadUint16FromBuf(TUint8*& aBuf) + { + // reads a 2 byte integer of expected endianess + TUint16 n = (*aBuf << 8)+ (*(aBuf+1)); + aBuf += sizeof(TUint16); + return n; + } + +TPtr8 TTraceDataParser::ReadTracePrintf(TUint8* aData, TInt aSize) + { + TPtr8 printfString(aData, aSize, aSize); + TInt endPosition = printfString.Locate(TChar(0)); + if (endPosition >= 0) + { + printfString = printfString.Left(endPosition); + } + else + { + printfString.Trim(); + } + return printfString; + } + +TBool TTraceDataParser::StringsMatch(const TDesC8& aString1, const TDesC8& aString2) + { + TInt compareLength = (aString1.Length() > aString2.Length()) ? aString2.Length() : aString1.Length(); + if ( (aString2.Left(compareLength).Compare(aString1) == 0) || + (aString1.Left(compareLength).Compare(aString2) == 0) ) + { + return ETrue; + } + return EFalse; + } + +/** + * Function to string for a number + * @param aBuffer Trace data descriptor + * @param aFindStringPattern Pattern that indicates where in the string the number will be, using an asterisk + * @param aNumberFound If found, the number in the string + * @return Symbian error code + */ +TInt TTraceDataParser::ParseStringForNumber(const TDesC8& aBuffer, const TDesC8& aFindStringPattern, TInt& aNumberFound) + { + TInt err = KErrNotFound; + TInt posOfNum = aFindStringPattern.Locate('*'); + TInt lengthOfNum = 1 + aBuffer.Length() - aFindStringPattern.Length(); + if (posOfNum >= 0 && lengthOfNum >= 1) + { + TBuf numberBuffer; + numberBuffer.Copy(aBuffer.Mid(posOfNum, lengthOfNum)); + TLex lex(numberBuffer); + err = lex.Val(aNumberFound); + } + return err; + } + +/* + * Validate the trace data in buffer + * + * @param aBuffer the buffer containing the trace data + * @param aExpectedNum expected number of traces + * @param aComponentID component ID of traces + * @param aGroupID group ID of traces + * @param aData first expected trace data + * @param aTraceWord trace word of traces + * @param aIdentical indicates that the payload of each trace packet should be identical + * @param aBufferMode The mode of the trace buffer.. 0=straight, 1=circular. + * @param aOrdinalData indicates whether the payload data reflects its position in the stream of trace packets + * @param aNumTraces number of traces written to circular buffer + * @param aMaxTraces maximum number of traces circular buffer can hold + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::ValidatePayload(TDesC8& aBuffer, + TInt aExpectedNum, + TComponentId aComponentID, + TGroupId aGroupID, + TUint32 aData, + TUint32 aTraceWord, + TBool aIdentical, + TInt aBufferMode, + TBool aOrdinalData, + TInt aNumTraces, + TInt aMaxTraces) + { + if ((aBuffer.Length() == 0) && (aExpectedNum <= 0)) // size is zero and there should be no traces + { + return KErrNone; + } + if ((aBuffer.Length() == 0) && (aExpectedNum > 0)) // size is zero and there should be traces + { + return KErrCorrupt; + } + if (aBuffer.Length() != 0 && (aExpectedNum <= 0)) // size is non-zero and there should be no traces + { + return KErrCorrupt; + } + + TUint32 lastData = aData; + TBool firstInStream = ETrue; + + // get the current position on the stream of traces so we can determine what the payload value should be. + TInt overwrites = 0; + if (aBufferMode == RTraceBuffer::EFreeRunning) + { + TInt overwrittenTraces = aNumTraces; // initialise to number of traces we wrote to the buffer for circular test + while (overwrittenTraces > aMaxTraces) + { + overwrittenTraces = overwrittenTraces - aMaxTraces; // aMaxTraces is the max the buffer can hold + overwrites++; + } + lastData = (aMaxTraces * (overwrites - 1)) + overwrittenTraces + 1; // get where we are in the buffer + } + + TInt numberOSTtraces = 0; + TUint8* data = (TUint8*) aBuffer.Ptr(); + TUint8* endOfData = data + aBuffer.Size(); + TUint8 testGID = aGroupID; + TUint32 testCID = aComponentID; + TUint32 testTraceWord = 0; + + TTraceHeaderSettings traceHeaderSettings; + + // Loop through all traces + while (data < endOfData) + { + // Get trace info from header + TInt err = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (err != KErrNone) + { + return err; + } + + //check the missing flag if we expect it + if ((firstInStream&&(overwrites>0))) + { + if(!(traceHeaderSettings.iHeaderFlags & BTrace::EMissingRecord)) + { + return KErrCorrupt; + } + } + + if (!traceHeaderSettings.iPrintfTrace) + { + testTraceWord = ((TUint32) (testGID << GROUPIDSHIFT)) + 1; + + // check group ID + if (traceHeaderSettings.iCategory != testGID) + { + return KErrCorrupt; + } + // check component ID + if (traceHeaderSettings.iComponentID != testCID) + { + return KErrCorrupt; + } + // check trace word + if (traceHeaderSettings.iTraceWord != testTraceWord) + { + return KErrCorrupt; + } + + TBool lastTrace = EFalse; + if ( (traceHeaderSettings.iMultiPartType == BTrace::EMultipartLast) || (!traceHeaderSettings.iMultiPartType) ) + { + lastTrace = ETrue; + } + + // check trace payload + for (TInt i=0; traceHeaderSettings.iLengthOfPayload>0; i++) + { + TUint32 param = Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + // For circular case, we try to estimate to within +/- 2 where we are in payload + // for straight case we should get it exactly correct. + if ( ((param != lastData) && (aBufferMode == 0)) || + ( ((param > lastData + 2) || (param < lastData - 2)) && (aBufferMode == RTraceBuffer::EFreeRunning) ) ) + { + return KErrCorrupt; + } + lastData = param; // in case we weren't accurate in circular case. + traceHeaderSettings.iLengthOfPayload -= 4; + lastData++; + } + lastData = (!aOrdinalData)? aData : lastData; + if (!aOrdinalData) + { + lastData = (lastTrace || aIdentical) ? aData : lastData; + } + + if (traceHeaderSettings.iMultiPartType || aIdentical || aOrdinalData) + { + testGID = aGroupID; + testCID = aComponentID; + testTraceWord = aTraceWord; + } + else + { + testGID++; + testCID++; + testTraceWord += (1 << GROUPIDSHIFT); + } + numberOSTtraces++; + } + else + { + // Go to the next trace + data += traceHeaderSettings.iLengthOfPayload; + } + firstInStream = EFalse; //after this, it's defnitely not the first trace in the stream. + } + + // Check that we're getting the correct number of traces + //or approximate ammount (+/-1) for circular buffer because + //the arithmetic to overwrite traces sometimes also skips one + //extra trace + + if (numberOSTtraces != aExpectedNum) + { + if ((aBufferMode==RTraceBuffer::EFreeRunning) + &&((numberOSTtraces>=aExpectedNum-1)||(numberOSTtraces<=aExpectedNum+1))) + { + return KErrNone; + } + return KErrCorrupt; + } + + return KErrNone; + } + +/* + * Validate the single trace data in buffer + * + * @param aBuffer the buffer containing the trace data + * @param aTracePresent determines if trace data should be present or not + * @param aGroupID expected GID + * @param aComponentID expected CID + * @param aData expected trace data + * @param aPrintfTrace determines if trace data should be printf data or not + * @param aMissingTrace determines if trace data should indicate missing data or not + * @param aExpectedPrintfTrace expected printf trace data + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::ValidatePayload(TDesC8& aBuffer, + TBool aTracePresent, + TGroupId aGroupID, + TComponentId aComponentID, + TUint32 aData, + TBool aPrintfTrace, + TBool aMissingTrace, + TDesC8* aExpectedPrintfTrace) + { + if ((aBuffer.Length() == 0) && (!aTracePresent)) // size is zero and there should be no traces + { + return KErrNone; + } + if ((aBuffer.Length() == 0) && (aTracePresent)) // size is zero and there should be traces + { + return KErrCorrupt; + } + if (aBuffer.Length() != 0 && (!aTracePresent)) // size is non-zero and there should be no traces + { + return KErrCorrupt; + } + + TUint8* data = (TUint8*) aBuffer.Ptr(); + TUint8* endOfData = data + aBuffer.Size(); + + TTraceHeaderSettings traceHeaderSettings; + + // Get trace info from header + TInt err = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (err != KErrNone) + { + return err; + } + + // check trace type + if (traceHeaderSettings.iPrintfTrace != aPrintfTrace) + { + return KErrCorrupt; + } + + if (!traceHeaderSettings.iPrintfTrace) + { + TUint32 testTraceWord = ((TUint32) (aGroupID << GROUPIDSHIFT)) + 1; + + // check group ID + if (traceHeaderSettings.iCategory != aGroupID) + { + return KErrCorrupt; + } + // check component ID + if (traceHeaderSettings.iComponentID != aComponentID) + { + return KErrCorrupt; + } + // check trace word + if (traceHeaderSettings.iTraceWord != testTraceWord) + { + return KErrCorrupt; + } + + if (aMissingTrace) + { + // check missing flag + if (!(traceHeaderSettings.iHeaderFlags & BTrace::EMissingRecord)) + { + return KErrCorrupt; + } + } + + TInt i = 0; + while (data < endOfData) + { + TUint32 param = Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + if (param != aData + i) + { + return KErrCorrupt; + } + i++; + } + } + else if (!aExpectedPrintfTrace) + { + return KErrArgument; + } + else + { + TBuf8 expectedPrintf; + + // In the case of an expected dropped trace, we only check for "* Dropped Trace" because + // the test writer has completed the request for data when it writes that bit first (right + // before the next trace), and when it goes to write the actual trace, it doesn't. + // If we change this, the "* Dropped Trace" will be overwritten by the actual trace before + // the test can check it, since the test writer doesn't buffer trace data. + if (aMissingTrace) + { + // check that "* Dropped Trace" is in the buffer + expectedPrintf = KDroppedTrace; + } + else + { + // check that "Test Printf Trace" is in the buffer" + expectedPrintf = *aExpectedPrintfTrace; + } + + expectedPrintf.Append(TChar(0)); // printf handler appends null at the end. + + TPtrC8 actualprintf(data, traceHeaderSettings.iLengthOfPayload); + + // Need to fix endianess for hw +#ifndef __WINS__ + TInt bufpos = 0; + while (bufpos < (actualprintf.Length()-4)) + { + // reads a 4 byte integer of expected endianess + TUint32 n = actualprintf[bufpos] + (actualprintf[bufpos+1] << 8) + (actualprintf[bufpos+2] << 16) + (actualprintf[bufpos+3] << 24); + bufpos += sizeof(TUint32); + TUint32 tempData = Swap(n); + memcpy((TAny*)(actualprintf.Ptr()+bufpos-4),(TAny*)&tempData, sizeof(TUint32)); + } +#endif //__WINS__ + + if (expectedPrintf.Compare(actualprintf) != 0) + { + return KErrCorrupt; + } + + // Check if there is another trace to follow + data += traceHeaderSettings.iLengthOfPayload; + if (data != endOfData) + { + TPtrC8 buffer(data, endOfData - data); + + return ValidatePayload(buffer, + aTracePresent, + aGroupID, + aComponentID, + aData, + aPrintfTrace, + EFalse, + aExpectedPrintfTrace); + } + } + return KErrNone; + } + +/* + * Verify the priming data contains all the expected values + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aCategory BTrace category of the trace data + * @param aSubCategory BTrace subcategory of the trace data + * @param aElementsFound Parameter to hold current state of expected data found so far + * @param aVerificationData1 Optional input value - used differently for different categories + * @param aVerificationData2 Optional input value - used differently for different categories + * @return Standard Symbian error code + */ +TInt TTraceDataParser::VerifyPrimingData(TUint8* aBuffer, + TInt aSize, + TUint8 aCategory, + TUint8 aSubCategory, + TInt& aElementsFound, + TBool aFromTestWriter, + TUint32 aVerificationData1, + TUint32 aVerificationData2) + { + TInt err = KErrNotSupported; + switch(aCategory) + { + case BTrace::EThreadIdentification: + err = VerifyThreadPrimingData(aBuffer, aSize, aSubCategory, aElementsFound, aFromTestWriter, aVerificationData1, aVerificationData2); + break; + case BTrace::EFastMutex: + err = VerifyFastMutexPrimingData(aBuffer, aSize, aSubCategory, aElementsFound, aFromTestWriter); + break; + case BTrace::ECodeSegs: + err = VerifyCodeSegsPrimingData(aBuffer, aSize, aSubCategory, aElementsFound, aFromTestWriter, aVerificationData1, aVerificationData2); + break; + default: + break; + } + return err; + } + +/* + * Verify the EThreadIdentification priming data contains all the expected values + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aSubCategory BTrace subcategory of the trace data + * @param aElementsFound Parameter to hold current state of expected data found so far + * @param aThreadAddr The address of the thread to search for + * @param aProcessAddr The address of the process to search for + * @return Standard Symbian error code + */ +TInt TTraceDataParser::VerifyThreadPrimingData(TUint8* aBuffer, + TInt aSize, + TUint8 aSubCategory, + TInt& aElementsFound, + TBool aFromTestWriter, + TUint32 aThreadAddr, + TUint32 aProcessAddr) + { + RThread currentThread; + RProcess currentProcess; + + TUint32 tempThreadAddr; + TUint32 tempProcessAddr; + + switch(aSubCategory) + { + case BTrace::EProcessCreate: + { + tempProcessAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if (tempProcessAddr == aProcessAddr) + { + aElementsFound |= 0x000000ff; + } + } + break; + + case BTrace::EProcessName: + { + tempThreadAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + tempProcessAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if ( (tempThreadAddr == aThreadAddr) && (tempProcessAddr == aProcessAddr) ) + { + TBuf8 currentProcessName; + currentProcessName.Copy(currentProcess.Name()); + TPtr8 processName(aBuffer,(aSize-8),(aSize-8)); //should be name of the process + processName.Trim(); + if (StringsMatch(currentProcessName, processName)) + { + aElementsFound |= 0x0000ff00; + } + } + } + break; + + case BTrace::EThreadName: + { + tempThreadAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + tempProcessAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if ( (tempThreadAddr == aThreadAddr) && (tempProcessAddr == aProcessAddr) ) + { + TBuf8 currentThreadName; + currentThreadName.Copy(currentThread.Name()); + TPtr8 threadName(aBuffer,(aSize-8),(aSize-8)); //should be name of the thread + threadName.Trim(); + if (StringsMatch(currentThreadName, threadName)) + { + aElementsFound |= 0x00ff0000; + } + } + } + break; + + case BTrace::EThreadId: + { + tempThreadAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + tempProcessAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if ( (tempThreadAddr == aThreadAddr) && (tempProcessAddr == aProcessAddr) ) + { + TThreadId threadId = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if (currentThread.Id() == threadId) + { + aElementsFound |= 0xff000000; + } + } + } + break; + + default: + break; + } + + TInt err = KErrNotFound; + if (aElementsFound == 0xffffffff) + { + err = KErrNone; + } + return err; + } + +/* + * Verify the EFastMutex priming data contains all the expected values + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aSubCategory BTrace subcategory of the trace data + * @param aElementsFound Parameter to hold current state of expected data found so far + * @return Standard Symbian error code + */ +TInt TTraceDataParser::VerifyFastMutexPrimingData(TUint8* aBuffer, + TInt aSize, + TUint8 aSubCategory, + TInt& aElementsFound, + TBool aFromTestWriter) + { + if (aSubCategory == BTrace::EFastMutexName) + { + TUint32 fastMutexId = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + TUint32 unspecified = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if (fastMutexId && !unspecified) + { + TPtr8 fastMutexName(aBuffer,(aSize-8),(aSize-8)); //should be name of the fast mutex + fastMutexName.Trim(); + if (StringsMatch(KFastMutexNameSystemLock(), fastMutexName)) + { + aElementsFound |= 0x000000ff; + } + else if (StringsMatch(KFastMutexNameMsgLock(), fastMutexName)) + { + aElementsFound |= 0x0000ff00; + } + else if (StringsMatch(KFastMutexNameObjLock(), fastMutexName)) + { + aElementsFound |= 0x00ff0000; + } + else if (StringsMatch(KFastMutexNameLogonLock(), fastMutexName)) + { + aElementsFound |= 0xff000000; + } + } + } + + TInt err = KErrNotFound; + if (aElementsFound == 0xffffffff) + { + err = KErrNone; + } + return err; + } + +/* + * Verify the ECodeSegs priming data contains all the expected values + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aSubCategory BTrace subcategory of the trace data + * @param aElementsFound Parameter to hold current state of expected data found so far + * @param aSegAddr1 The address of the first code seg to search for + * @param aSegAddr2 The address of the second code seg to search for + * @return Standard Symbian error code + */ +TInt TTraceDataParser::VerifyCodeSegsPrimingData(TUint8* aBuffer, + TInt aSize, + TUint8 aSubCategory, + TInt& aElementsFound, + TBool aFromTestWriter, + TUint32 aSegAddr1, + TUint32 aSegAddr2) + { + TUint32 codeSegAddr; + if (aSubCategory == BTrace::ECodeSegCreated) + { + codeSegAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if (codeSegAddr == aSegAddr1 || codeSegAddr == aSegAddr2) + { + TPtr8 codeSegName(aBuffer,(aSize-4),(aSize-4)); //should be name of the code seg + codeSegName.Trim(); + if (codeSegAddr == aSegAddr1 && StringsMatch(KCodeSegsName1(), codeSegName)) + { + aElementsFound |= 0x000000ff; + } + else if (codeSegAddr == aSegAddr2 && StringsMatch(KCodeSegsName2(), codeSegName)) + { + aElementsFound |= 0x0000ff00; + } + } + } + else if (aSubCategory == BTrace::ECodeSegInfo) + { + codeSegAddr = Swap(ReadUint32FromBuf(aBuffer, aFromTestWriter)); + if (codeSegAddr == aSegAddr1) + { + aElementsFound |= 0x00ff0000; + } + else if (codeSegAddr == aSegAddr2) + { + aElementsFound |= 0xff000000; + } + } + + TInt err = KErrNotFound; + if (aElementsFound == 0xffffffff) + { + err = KErrNone; + } + return err; + } + +/* + * Work out if a trace point is potentially kernel priming data based on category and subcategory + * + * @param aCategory BTrace category of the trace data + * @param aSubCategory BTrace subcategory of the trace data + * @param aIsPotentialPrimingTrace Output value set if there is no error + * @return Standard Symbian error code + */ +TBool TTraceDataParser::IsPotentialPrimingTrace(TUint8 aCategory, TUint8 aSubCategory, TBool& aIsPotentialPrimingTrace) + { + TInt err = KErrNone; + + switch(aCategory) + { + case BTrace::EThreadIdentification: + { + switch(aSubCategory) + { + case BTrace::EProcessCreate: + case BTrace::EProcessName: + case BTrace::EThreadName: + case BTrace::EThreadId: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::ECpuUsage: + { + switch(aSubCategory) + { + case BTrace::ENewThreadContext: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::EChunks: + { + switch(aSubCategory) + { + case BTrace::EChunkCreated: + case BTrace::EChunkOwner: + case BTrace::EChunkInfo: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::ECodeSegs: + { + switch(aSubCategory) + { + case BTrace::ECodeSegCreated: + case BTrace::ECodeSegInfo: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::EPaging: + { + switch(aSubCategory) + { + case BTrace::EPagingMemoryModel: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::EThreadPriority: + { + switch(aSubCategory) + { + case BTrace::EProcessPriority: + case BTrace::EDThreadPriority: + case BTrace::ENThreadPriority: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::EFastMutex: + { + switch(aSubCategory) + { + case BTrace::EFastMutexName: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::ESymbianKernelSync: + { + switch(aSubCategory) + { + case BTrace::EMutexCreate: + case BTrace::ESemaphoreCreate: + case BTrace::ECondVarCreate: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + case BTrace::EClientServer: + { + switch(aSubCategory) + { + case BTrace::EServerCreate: + case BTrace::ESessionAttach: + aIsPotentialPrimingTrace = ETrue; + break; + default: + aIsPotentialPrimingTrace = EFalse; + break; + } + } + break; + default: + err = KErrNotSupported; + break; + } + + return err; + } + +/* + * If the next trace is a meta trace true is returned if it is the start of priming data for a given group ID + * + * @param aBuffer Buffer containing the trace data + * @param aGroupId Group ID of kernel priming data + * @param aCategory BTrace category of the current trace + * @param aSubCategory BTrace subcategory of the current trace + * @return True if start of priming data for given group ID, false otherwise + */ +TBool TTraceDataParser::IsStartOfKernelPrimingBatch(TUint8* aBuffer, TGroupId aGroupId, TUint8 aCategory, TUint8 aSubCategory) + { + TBool startOfBatch = EFalse; + if (aCategory == BTrace::EMetaTrace) + { + if (aSubCategory == BTrace::EMetaTraceFilterChange) + { + TUint8 metaCategory = aBuffer[0]; + TUint8 isActive = aBuffer[1]; + if (metaCategory == aGroupId && isActive) + { + startOfBatch = ETrue; + } + } + } + return startOfBatch; + } + +/* + * Use heuristics to work out if current trace is part of kernel priming data + * + * @param aCategory BTrace category of the trace data + * @param aSubCategory BTrace subcategory of the trace data + * @param aTimeDifference Time difference (in microseconds) since last trace for given category + * @param aIsInPrimingBatch Output parameter set to true if in priming data + * @param aFirstTrace Boolean used to indicate if this is first trace (so there is no valid time difference) + * @param aStartOfBatch Output parameter set to true if start of batch of kernel priming data + * @return Standard Symbian error code + */ +TInt TTraceDataParser::IsInPrimingBatch(TUint8 aCategory, + TUint8 aSubCategory, + TUint32 aTimeDifference, + TBool& aIsInPrimingBatch, + TBool& aFirstTrace, + TBool& aStartOfBatch) + { + TBool isPotentialPrimingTrace = EFalse; + TInt err = IsPotentialPrimingTrace(aCategory, aSubCategory, isPotentialPrimingTrace); + + // Look at timestamp to see if this is a new batch of traces + if (aFirstTrace) + { + aFirstTrace = EFalse; + } + else if (aTimeDifference > KMinMilliSecondsBatchGap * 1000) + { + aStartOfBatch = ETrue; + aIsInPrimingBatch = EFalse; + } + else + { + aStartOfBatch = EFalse; + } + + if (isPotentialPrimingTrace && aStartOfBatch) + { + aIsInPrimingBatch = ETrue; + aStartOfBatch = EFalse; + } + else if (!isPotentialPrimingTrace) + { + aIsInPrimingBatch = EFalse; + } + + return err; + } + +/* + * Parse the trace data header + * + * The following trace types are supported: + * OST traces (ascii and binary) + * XTIv3 traces (ascii and binary) + * Traces from the Test Writer (ascii and binary) + * + * @param aData Buffer containing the trace data + * @param aSize Size of trace data buffer + * @param aTraceHeaderSettings Output parameters set to header settings of the trace data + * @return Standard Symbian error code + */ +TInt TTraceDataParser::ParseHeader(TUint8*& aData, + TInt aSize, + TTraceHeaderSettings& aTraceHeaderSettings) + { + // Check buffer is large enough to contain header + aSize--; + if (aSize < 0) + { + return KErrOverflow; + } + + TInt err = KErrNone; + TUint8* startOfData = aData; + aTraceHeaderSettings.iMultiPartType = 0; + aTraceHeaderSettings.iMultiPartTraceID = 0; + aTraceHeaderSettings.iPrintfTrace = EFalse; + aTraceHeaderSettings.iHeaderFlags = EFalse; + aTraceHeaderSettings.iFromTestWriter = EFalse; + TBool xtiTrace = EFalse; + TUint8 version = aData[0]; // Version (1 byte) + aData++; + + if (version == KXtiHeaderVersion) + { + // Check buffer is large enough to contain header + aSize -= KMinSizeXtiHeader; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + xtiTrace = ETrue; + TUint8 xtiTraceType = aData[KXtiTraceTypeIndex-1]; + if (xtiTraceType != KXtiProtocolIdSimpleTrace) + { + aTraceHeaderSettings.iPrintfTrace = ETrue; + aTraceHeaderSettings.iLengthOfPayload = aData[KXtiLengthIndex-1] - 15; + aData += KMinSizeXtiHeader; + } + else + { + // Check buffer is large enough to contain header + aSize -= 1; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + // Jump to start of OST buffer + aData += KMinSizeXtiHeader + 1; + version = aData[0]; + aData++; + } + } + + if (version == KHeaderVersion) // Ost header version byte is correct + { + // Check buffer is large enough to contain header + aSize -= KMinSizeOstHeader; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + const TUint8 protocolId = aData[0]; // Protocol ID (1 byte) + aData++; + if (xtiTrace) + { + aTraceHeaderSettings.iLengthOfPayload = aData[0]; + aData++; + aSize++; + } + else + { + aTraceHeaderSettings.iLengthOfPayload = ReadUint16FromBuf(aData); // Size (2 bytes) + } + + if (protocolId == KProtocolIdSimpleTrace) // Normal binary data + { + // Check buffer is large enough to contain header + aSize -= KMinSizeOstBinaryHeader; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + ReadUint32FromBuf(aData); // Timestamp 1 (4 bytes) + aTraceHeaderSettings.iTimestamp = ReadUint32FromBuf(aData); // Timestamp 2 (4 bytes) + } + else if (protocolId == KProtocolIdAscii) // Ascii Printf data + { + aTraceHeaderSettings.iPrintfTrace = ETrue; + // Check buffer is large enough to contain header + aSize -= KMinSizeOstAsciiHeader; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + aData += 8; + aTraceHeaderSettings.iLengthOfPayload -= 8; + } + else // Protocol ID is incorrect + { + err = KErrCorrupt; + } + } + else if (version == SYMBIAN_TRACE) // Trace data is binary data from Test Writer + { + aTraceHeaderSettings.iFromTestWriter = ETrue; + } + else if (version == PRINTF_TRACE) // Trace data is ascii data from Test Writer + { + aTraceHeaderSettings.iFromTestWriter = ETrue; + aTraceHeaderSettings.iPrintfTrace = ETrue; + // The end of the printf trace data will have null char, so use that to find end of payload + TPtr8 printfString(aData, aSize, aSize); + aTraceHeaderSettings.iLengthOfPayload = printfString.Locate(TChar(0)); + if (aTraceHeaderSettings.iLengthOfPayload >= 0) + { + aTraceHeaderSettings.iLengthOfPayload++; + } + else + { + err = KErrCorrupt; + } + } + else if (version != KXtiHeaderVersion) + { + err = KErrCorrupt; + + if(aTraceHeaderSettings.iLengthOfPayload < 0 ) + { + err = KErrOverflow; + } + } + + if (aTraceHeaderSettings.iPrintfTrace) + { + aTraceHeaderSettings.iTimestamp = 0; + aTraceHeaderSettings.iComponentID = 0; + aTraceHeaderSettings.iCategory = 0; + aTraceHeaderSettings.iSubCategory = 0; + aTraceHeaderSettings.iTraceWord = 0; + } + else if (err == KErrNone) + { + // Check buffer is large enough to contain header + aSize -= KMinSizeBinaryHeader; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + aTraceHeaderSettings.iComponentID = ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter); // ComponentId (4 bytes) + aTraceHeaderSettings.iTraceWord = ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter); // GroupId (2 bytes). TraceId (2 bytes) + + TUint8* startOfPayload = aData; + TUint8 size; + + TBool testWriterHwData = EFalse; +#ifndef __WINS__ + testWriterHwData = aTraceHeaderSettings.iFromTestWriter; +#endif + + // now look at BTrace header and payload. + if (!testWriterHwData) + { + size = aData[BTrace::ESizeIndex]; + aTraceHeaderSettings.iHeaderFlags = aData[BTrace::EFlagsIndex]; + aTraceHeaderSettings.iCategory = aData[BTrace::ECategoryIndex]; + aTraceHeaderSettings.iSubCategory = aData[BTrace::ESubCategoryIndex]; + } + else + { + // endianess order is reversed for TestWriter on hw + aTraceHeaderSettings.iSubCategory = aData[BTrace::ESizeIndex]; + aTraceHeaderSettings.iCategory = aData[BTrace::EFlagsIndex]; + aTraceHeaderSettings.iHeaderFlags = aData[BTrace::ECategoryIndex]; + size = aData[BTrace::ESubCategoryIndex]; + } + + aData+=4; + + //read header extensions + if(aTraceHeaderSettings.iHeaderFlags & BTrace::EHeader2Present) + { + // Check buffer is large enough to contain header + aSize -= 4; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + TUint32 header2 = Swap(ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter)); + aTraceHeaderSettings.iMultiPartType = header2 & BTrace::EMultipartFlagMask; + } + if(aTraceHeaderSettings.iHeaderFlags & BTrace::ETimestampPresent) + { + // Check buffer is large enough to contain header + aSize -= 4; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter); + } + if(aTraceHeaderSettings.iHeaderFlags & BTrace::ETimestamp2Present) + { + // Check buffer is large enough to contain header + aSize -= 4; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter); + } + if(aTraceHeaderSettings.iHeaderFlags & BTrace::EContextIdPresent) + { + // Check buffer is large enough to contain header + aSize -= 4; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter); + } + if(aTraceHeaderSettings.iHeaderFlags & BTrace::EPcPresent) + { + // Check buffer is large enough to contain header + aSize -= 4; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter); + } + if(aTraceHeaderSettings.iHeaderFlags & BTrace::EExtraPresent) + { + // Check buffer is large enough to contain header + aSize -=4 ; + if (aSize < 0) + { + aData = startOfData; + return KErrOverflow; + } + aTraceHeaderSettings.iMultiPartTraceID = Swap(ReadUint32FromBuf(aData, aTraceHeaderSettings.iFromTestWriter)); + } + + if (!xtiTrace) + { + size = (size+3)&~3; // make it 4 byte aligned + } + if (size == 0) + { + // This happens when the trace is larger than maximum size, and we can't use the BTrace size field + // Instead calculate payload length based on size in OST header + aTraceHeaderSettings.iLengthOfPayload -= (aData-startOfPayload)+16; // size of rest of payload + } + else + { + aTraceHeaderSettings.iLengthOfPayload = size-(aData-startOfPayload); // size of rest of payload + } + } + + + // Check buffer is large enough to contain payload + aSize -= aTraceHeaderSettings.iLengthOfPayload; + if (aSize < 0) + { + aData = startOfData; + err = KErrOverflow; + } + + return err; + } + +/* + * Get the data required to verify kernel priming traces for EThreadIdentification + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aThreadAddr Output parameter set to address of current thread + * @param aProcessAddr Output parameter set to address of current process + * @return Standard Symbian error code + */ +TInt TTraceDataParser::GetThreadPrimingVerificationDataL(TUint8* aBuffer, + TInt aSize, + TUint32& aThreadAddr, + TUint32& aProcessAddr, + RFile* aFile) + { + TInt err = KErrNone; + TInt filePos = 0; + TUint8* data = NULL; + TUint8* startOfData = NULL; + TUint8* endOfData = NULL; + RBuf8 fileBuffer; + + if (aFile) + { + // Create file buffer and read first chunk from file + err = CreateFileBuffer(fileBuffer, *aFile); + if (err != KErrNone) + { + return err; + } + fileBuffer.CleanupClosePushL(); + err = ReadNextChunkFromFile(fileBuffer, *aFile, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + return err; + } + } + else + { + data = aBuffer; + startOfData = data; + endOfData = data + aSize; + } + + TInt headerErr = KErrNone; + RThread currentThread; + TUint32 tempThreadAddr; + TUint32 tempProcessAddr; + TThreadId threadid = 0; + + TTraceHeaderSettings traceHeaderSettings; + + err = KErrGeneral; + + while(data < endOfData) + { + headerErr = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (aFile && headerErr == KErrOverflow) + { + // We don't have all the trace data, so read next chunk from file + headerErr = ReadNextChunkFromFile(fileBuffer, *aFile, filePos, data, startOfData, endOfData); + if (headerErr == KErrNone) + { + continue; + } + } + if (headerErr != KErrNone) + { + err = headerErr; + break; + } + if ( (traceHeaderSettings.iCategory == BTrace::EThreadIdentification) && (traceHeaderSettings.iSubCategory == BTrace::EThreadId) ) + { + tempThreadAddr = Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + tempProcessAddr = Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + threadid = Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + if (currentThread.Id() == threadid) + { + aThreadAddr = tempThreadAddr; + aProcessAddr = tempProcessAddr; + err = KErrNone; + break; + } + } + else + { + data += traceHeaderSettings.iLengthOfPayload; //go to next trace + } + + if (aFile && data == endOfData) + { + // We might not have all the trace data, so read next chunk from file + headerErr = ReadNextChunkFromFile(fileBuffer, *aFile, filePos, data, startOfData, endOfData); + if (headerErr != KErrNone) + { + err = headerErr; + break; + } + } + } + + if (aFile) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + } + + return err; + } + + +/* + * Get the data required to verify kernel priming traces for ECodeSegs + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aSegAddr1 Output parameter set to address of first code seg + * @param aSegAddr2 Output parameter set to address of second code seg + * @return Standard Symbian error code + */ +TInt TTraceDataParser::GetCodeSegsVerificationDataL(TUint8* aBuffer, + TInt aSize, + TUint32& aSegAddr1, + TUint32& aSegAddr2, + RFile* aFile) + { + TInt err = KErrNone; + TInt filePos = 0; + TUint8* data = NULL; + TUint8* startOfData = NULL; + TUint8* endOfData = NULL; + RBuf8 fileBuffer; + + if (aFile) + { + // Create file buffer and read first chunk from file + err = CreateFileBuffer(fileBuffer, *aFile); + if (err != KErrNone) + { + return err; + } + fileBuffer.CleanupClosePushL(); + err = ReadNextChunkFromFile(fileBuffer, *aFile, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + return err; + } + } + else + { + data = aBuffer; + startOfData = data; + endOfData = data + aSize; + } + + TInt headerErr = KErrNone; + TUint32 tempSegAddr; + + TTraceHeaderSettings traceHeaderSettings; + + err = KErrGeneral; + + while(data < endOfData) + { + headerErr = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (aFile && headerErr == KErrOverflow) + { + // We don't have all the trace data, so read next chunk from file + headerErr = ReadNextChunkFromFile(fileBuffer, *aFile, filePos, data, startOfData, endOfData); + if (headerErr == KErrNone) + { + continue; + } + } + if (headerErr != KErrNone) + { + err = headerErr; + break; + } + if ( (traceHeaderSettings.iCategory == BTrace::ECodeSegs) && (traceHeaderSettings.iSubCategory == BTrace::ECodeSegCreated) ) + { + tempSegAddr = Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + traceHeaderSettings.iLengthOfPayload -= 4; + TPtr8 codeSegName(data,traceHeaderSettings.iLengthOfPayload,traceHeaderSettings.iLengthOfPayload); //should be name of the code seg + codeSegName.Trim(); + if (StringsMatch(KCodeSegsName1(), codeSegName)) + { + aSegAddr1 = tempSegAddr; + } + if (StringsMatch(KCodeSegsName2(), codeSegName)) + { + aSegAddr2 = tempSegAddr; + } + if (aSegAddr1 != 0 && aSegAddr2 != 0) + { + err = KErrNone; + break; + } + } + + data += traceHeaderSettings.iLengthOfPayload; //go to next trace + + if (aFile && data == endOfData) + { + // We might not have all the trace data, so read next chunk from file + headerErr = ReadNextChunkFromFile(fileBuffer, *aFile, filePos, data, startOfData, endOfData); + if (headerErr != KErrNone) + { + err = headerErr; + break; + } + } + } + + if (aFile) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + } + + return err; + } + +/* + * Get the data required to verify kernel priming traces + * + * @param aBuffer Buffer containing the trace data + * @param aSize Size of the trace buffer + * @param aGroupId Group ID of traces + * @param aVerificationData1 Output parameter set to first data required + * @param aVerificationData2 Output parameter set to second data required + * @return Standard Symbian error code + */ +TInt TTraceDataParser::GetPrimingVerificationDataL(TUint8* aBuffer, + TInt aSize, + TGroupId aGroupId, + TUint32& aVerificationData1, + TUint32& aVerificationData2, + RFile* aFile) + { + TInt err = KErrNone; + switch(aGroupId) + { + case BTrace::EThreadIdentification: + err = GetThreadPrimingVerificationDataL(aBuffer, aSize, aVerificationData1, aVerificationData2, aFile); + break; + case BTrace::ECodeSegs: + err = GetCodeSegsVerificationDataL(aBuffer, aSize, aVerificationData1, aVerificationData2, aFile); + break; + default: + break; + } + return err; + } + +/* + * Parse trace data for kernel priming data + * + * Note that this function can only verify priming data if the group ID is EThreadIdentification or EFastMutex, + * otherwise KErrNotSupported is returned if aVerifyData is ETrue + * + * @param aBuffer Buffer containing the trace data + * @param aGroupId Group ID of traces to parse + * @param aNumPrimingTraces Output parameter set to number of priming traces found for given group ID + * @param aNumTraces Output parameter set to total number of traces found for given group ID + * @param aVerifyData Flag indicating if priming data should be verified + * @param aVerificationData1 Optional first data required for verifying priming data + * @param aVerificationData1 Optional second data required for verifying priming data + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::ParsePrimingDataL(TDesC8& aBuffer, + TGroupId aGroupId, + TInt& aNumPrimingTraces, + TInt& aNumTraces, + TBool aVerifyData, + TUint32 aVerificationData1, + TUint32 aVerificationData2) + { + aNumPrimingTraces = 0; + aNumTraces = 0; + + TUint8* data = (TUint8*) aBuffer.Ptr(); + TUint8* endOfData = data + aBuffer.Size(); + + // If the output data is to be verified but no verification data provided, try getting it automatically + if (aVerifyData && aVerificationData1 == 0 && aVerificationData2 == 0) + { + TInt err = GetPrimingVerificationDataL(data, endOfData-data, aGroupId, aVerificationData1, aVerificationData2); + if (err != KErrNone) + { + return err; + } + } + + TInt returnErr = KErrNone; + TInt dataVerificationErr = KErrNone; + TInt generalErr = KErrNone; + TBool firstTrace = ETrue; + TBool startOfBatch = ETrue; + TBool inPrimingData = EFalse; + TBool useMetaTraces = EFalse; + TInt numPotentialPrimingTraces = 0; + TUint32 lastTimestamp = 0; + TInt elementsFound = 0; + + TTraceHeaderSettings traceHeaderSettings; + + // Loop through all traces + while (data < endOfData) + { + // Get trace info from header + generalErr = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (generalErr != KErrNone) + { + if (returnErr == KErrNone) + { + // Set error to return, if this is first error so far + returnErr = generalErr; + } + break; + } + + // If this is a meta trace, work out if it signifies start of kernel priming data for GID of interest + if (traceHeaderSettings.iCategory == BTrace::EMetaTrace) + { + useMetaTraces = ETrue; + startOfBatch = IsStartOfKernelPrimingBatch(data, aGroupId, traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory); + } + + // This is a trace of the correct GID/CID + if ( (traceHeaderSettings.iComponentID == KKernelHooksOSTComponentUID) && (traceHeaderSettings.iCategory == aGroupId) ) + { + // Increase the number of traces for this GID + aNumTraces++; + + if (useMetaTraces) + { + // If data contains meta traces, work out if this is a priming trace + if (startOfBatch) + { + // This is the start of a batch of priming traces + inPrimingData = ETrue; + startOfBatch = EFalse; + } + if (inPrimingData) + { + // This is a priming trace, so increase number found for this GID + aNumPrimingTraces++; + // If data is to be verified, attempt the verification + if (aVerifyData) + { + dataVerificationErr = VerifyPrimingData(data, traceHeaderSettings.iLengthOfPayload, traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory, elementsFound, traceHeaderSettings.iFromTestWriter, aVerificationData1, aVerificationData2); + } + } + } + else + { + // If data contains no meta traces, work out if this is a priming trace using heuristics + generalErr = IsInPrimingBatch(traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory, traceHeaderSettings.iTimestamp-lastTimestamp, inPrimingData, firstTrace, startOfBatch); + if ( (generalErr != KErrNone) && (returnErr == KErrNone) ) + { + // Set error to return, if this is first error so far + returnErr = generalErr; + } + lastTimestamp = traceHeaderSettings.iTimestamp; + if (inPrimingData) + { + // This is possibly a priming trace, so increase potential number found for this GID + numPotentialPrimingTraces++; + // If data is to be verified, attempt the verification + if (aVerifyData) + { + dataVerificationErr = VerifyPrimingData(data, traceHeaderSettings.iLengthOfPayload, traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory, elementsFound, traceHeaderSettings.iFromTestWriter, aVerificationData1, aVerificationData2); + } + } + else + { + // If we have enough potential priming traces, assume they are priming traces + if (numPotentialPrimingTraces >= KMinimumPrimingTracesInBatch) + { + aNumPrimingTraces += numPotentialPrimingTraces; + } + numPotentialPrimingTraces = 0; + } + } + } + else + { + // This is not a trace of the correct GID/CID + inPrimingData = EFalse; + } + + // Go to the next trace + data+=traceHeaderSettings.iLengthOfPayload; + } + + if (!useMetaTraces) + { + // If we have enough potential priming traces left over, assume they are priming traces + if (numPotentialPrimingTraces >= KMinimumPrimingTracesInBatch) + { + aNumPrimingTraces += numPotentialPrimingTraces; + } + } + + if ( (dataVerificationErr != KErrNone) && (returnErr == KErrNone) ) + { + // Set error to return, if this is first error so far + returnErr = dataVerificationErr; + } + return returnErr; + } + +/* + * Parse trace data for kernel priming data + * + * Note that this function can only verify priming data if the group ID is EThreadIdentification or EFastMutex, + * otherwise KErrNotSupported is returned if aVerifyData is ETrue + * + * @param aFilePath Full path of file containing the trace data + * @param aFs File system object + * @param aGroupId Group ID of traces to parse + * @param aNumPrimingTraces Output parameter set to number of priming traces found for given group ID + * @param aNumTraces Output parameter set to total number of traces found for given group ID + * @param aVerifyData Flag indicating if priming data should be verified + * @param aVerificationData1 Optional first data required for verifying priming data + * @param aVerificationData1 Optional second data required for verifying priming data + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::ParsePrimingDataL(const TDesC& aFilePath, + RFs& aFs, + TGroupId aGroupId, + TInt& aNumPrimingTraces, + TInt& aNumTraces, + TBool aVerifyData, + TUint32 aVerificationData1, + TUint32 aVerificationData2) + { + aNumPrimingTraces = 0; + aNumTraces = 0; + + RFile file; + + // Open file with trace data + TInt err = file.Open(aFs, aFilePath, EFileRead|EFileShareAny); + if (err != KErrNone) + { + return err; + } + CleanupClosePushL(file); + + // If the output data is to be verified but no verification data provided, try getting it automatically + if (aVerifyData && aVerificationData1 == 0 && aVerificationData2 == 0) + { + err = GetPrimingVerificationDataL(NULL, 0, aGroupId, aVerificationData1, aVerificationData2, &file); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + } + + RBuf8 fileBuffer; + TInt filePos = 0; + + // Create file buffer and read first chunk from file + err = CreateFileBuffer(fileBuffer, file); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + fileBuffer.CleanupClosePushL(); + + TUint8* data = NULL; + TUint8* startOfData = NULL; + TUint8* endOfData = NULL; + + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + + TInt dataVerificationErr = KErrNone; + TInt generalErr = KErrNone; + TBool firstTrace = ETrue; + TBool startOfBatch = ETrue; + TBool inPrimingData = EFalse; + TBool useMetaTraces = EFalse; + TInt numPotentialPrimingTraces = 0; + TUint32 lastTimestamp = 0; + TInt elementsFound = 0; + + TTraceHeaderSettings traceHeaderSettings; + + // Loop through all traces + while (data < endOfData) + { + // Get trace info from header + generalErr = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (generalErr == KErrOverflow) + { + // We don't have all the trace data, so read next chunk from file + generalErr = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (generalErr == KErrNone) + { + continue; + } + } + if (generalErr != KErrNone) + { + if (err == KErrNone) + { + // Set error to return, if this is first error so far + err = generalErr; + } + break; + } + + // If this is a meta trace, work out if it signifies start of kernel priming data for GID of interest + if (traceHeaderSettings.iCategory == BTrace::EMetaTrace) + { + useMetaTraces = ETrue; + startOfBatch = IsStartOfKernelPrimingBatch(data, aGroupId, traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory); + } + + // This is a trace of the correct GID/CID + if ( (traceHeaderSettings.iComponentID == KKernelHooksOSTComponentUID) && (traceHeaderSettings.iCategory == aGroupId) ) + { + // Increase the number of traces for this GID + aNumTraces++; + + if (useMetaTraces) + { + // If data contains meta traces, work out if this is a priming trace + if (startOfBatch) + { + // This is the start of a batch of priming traces + inPrimingData = ETrue; + startOfBatch = EFalse; + } + if (inPrimingData) + { + // This is a priming trace, so increase number found for this GID + aNumPrimingTraces++; + // If data is to be verified, attempt the verification + if (aVerifyData) + { + dataVerificationErr = VerifyPrimingData(data, traceHeaderSettings.iLengthOfPayload, traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory, elementsFound, traceHeaderSettings.iFromTestWriter, aVerificationData1, aVerificationData2); + } + } + } + else + { + // If data contains no meta traces, work out if this is a priming trace using heuristics + generalErr = IsInPrimingBatch(traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory, traceHeaderSettings.iTimestamp-lastTimestamp, inPrimingData, firstTrace, startOfBatch); + if ( (generalErr != KErrNone) && (err == KErrNone) ) + { + // Set error to return, if this is first error so far + err = generalErr; + } + lastTimestamp = traceHeaderSettings.iTimestamp; + if (inPrimingData) + { + // This is possibly a priming trace, so increase potential number found for this GID + numPotentialPrimingTraces++; + // If data is to be verified, attempt the verification + if (aVerifyData) + { + dataVerificationErr = VerifyPrimingData(data, traceHeaderSettings.iLengthOfPayload, traceHeaderSettings.iCategory, traceHeaderSettings.iSubCategory, elementsFound, traceHeaderSettings.iFromTestWriter, aVerificationData1, aVerificationData2); + } + } + else + { + // If we have enough potential priming traces, assume they are priming traces + if (numPotentialPrimingTraces >= KMinimumPrimingTracesInBatch) + { + aNumPrimingTraces += numPotentialPrimingTraces; + } + numPotentialPrimingTraces = 0; + } + } + } + else + { + // This is not a trace of the correct GID/CID + inPrimingData = EFalse; + } + + // Go to the next trace + data+=traceHeaderSettings.iLengthOfPayload; + + if (data == endOfData) + { + // We might not have all the trace data, so read next chunk from file + generalErr = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (generalErr != KErrNone) + { + if (err == KErrNone) + { + // Set error to return, if this is first error so far + err = generalErr; + } + break; + } + } + } + + if (!useMetaTraces) + { + // If we have enough potential priming traces left over, assume they are priming traces + if (numPotentialPrimingTraces >= KMinimumPrimingTracesInBatch) + { + aNumPrimingTraces += numPotentialPrimingTraces; + } + } + + if ( (dataVerificationErr != KErrNone) && (err == KErrNone) ) + { + // Set error to return, if this is first error so far + err = dataVerificationErr; + } + + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + CleanupStack::PopAndDestroy(&file); // close file + + return err; + } + +TInt TTraceDataParser::CreateFileBuffer(RBuf8& aFileBuffer, + RFile& aFile) + { + TInt fileSize; + TInt err = aFile.Size(fileSize); + if (err == KErrNone) + { + if (fileSize < KFileBufferSize) + { + err = aFileBuffer.Create(fileSize); + } + else + { + err = aFileBuffer.Create(KFileBufferSize); + } + } + return err; + } + +TInt TTraceDataParser::ReadNextChunkFromFile(TDes8& aFileBuffer, + RFile& aFile, + TInt& aFilePosition, + TUint8*& aData, + TUint8*& aStartOfData, + TUint8*& aEndOfData) + { + aFilePosition += aData-aStartOfData; + TInt err = aFile.Read(aFilePosition, aFileBuffer); + if (err == KErrNone) + { + aData = (TUint8*) aFileBuffer.Ptr(); + aStartOfData = aData; + aEndOfData = aData + aFileBuffer.Size(); + } + return err; + } + +/* + * Parse trace data for Printf data, returning the number of occurances of a given string + * + * @param aBuffer Buffer containing the trace data + * @param aFindString String to search for + * @param aNumFound Output parameter set to number of occurances of given string + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::DataHasPrintfString(TDesC8& aBuffer, + const TDesC8& aFindString, + TInt& aNumFound) + { + aNumFound = 0; + + TInt err = KErrNone; + TUint8* data = (TUint8*) aBuffer.Ptr(); + TUint8* endOfData = data + aBuffer.Size(); + + TTraceHeaderSettings traceHeaderSettings; + + while(data < endOfData) + { + err = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (err != KErrNone) + { + break; + } + if (traceHeaderSettings.iPrintfTrace) + { + TPtr8 printfString = ReadTracePrintf(data, traceHeaderSettings.iLengthOfPayload); + if (StringsMatch(aFindString, printfString)) + { + aNumFound++; + } + } + data += traceHeaderSettings.iLengthOfPayload; //go to next trace + } + + return err; + } + +/* + * Parse trace data file for Printf data, returning the number of occurances of a given string + * + * @param aFilePath Full path of file containing the trace data + * @param aFs File system object + * @param aFindString String to search for + * @param aNumFound Output parameter set to number of occurances of given string + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::DataHasPrintfStringL(const TDesC& aFilePath, + RFs& aFs, + const TDesC8& aFindString, + TInt& aNumFound) + { + aNumFound = 0; + + RFile file; + + // Open file with trace data + TInt err = file.Open(aFs, aFilePath, EFileRead|EFileShareAny); + if (err != KErrNone) + { + return err; + } + CleanupClosePushL(file); + + RBuf8 fileBuffer; + TInt filePos = 0; + + // Create file buffer and read first chunk from file + err = CreateFileBuffer(fileBuffer, file); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + fileBuffer.CleanupClosePushL(); + + TUint8* data = NULL; + TUint8* startOfData = NULL; + TUint8* endOfData = NULL; + + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + + TTraceHeaderSettings traceHeaderSettings; + + while(data < endOfData) + { + err = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (err == KErrOverflow) + { + // We don't have all the trace data, so read next chunk from file + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err == KErrNone) + { + continue; + } + } + if (err != KErrNone) + { + break; + } + if (traceHeaderSettings.iPrintfTrace) + { + TPtr8 printfString = ReadTracePrintf(data, traceHeaderSettings.iLengthOfPayload); + if (StringsMatch(aFindString, printfString)) + { + aNumFound++; + } + } + data += traceHeaderSettings.iLengthOfPayload; //go to next trace + + if (data == endOfData) + { + // We might not have all the trace data, so read next chunk from file + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + break; + } + } + } + + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + CleanupStack::PopAndDestroy(&file); // close file + + return err; + } + +/* + * Parse trace data file for a sequence of traces + * + * @param aFilePath Full path of file containing the trace data + * @param aFs File system object + * @param aLastNumberFound Output parameter set to number in last occurances of given string + * @param aNumDroppedTraces Output parameter set to number of dropped traces + * @param aFindPrintfPattern Pattern that indicates where in the printf string the number will be, using an asterisk + * This is only used if aGroupId = BTrace::ERDebugPrintf + * @param aGroupId Group ID of traces to parse + * @param aComponentID Component ID of traces to parse + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::DataHasTraceSequenceL(const TDesC& aFilePath, + RFs& aFs, + TInt& aLastNumberFound, + TInt& aNumDroppedTraces, + TDesC8* aFindPrintfPattern, + TGroupId aGroupId, + TComponentId aComponentID) + { + aLastNumberFound = 0; + aNumDroppedTraces = 0; + + RFile file; + + // Open file with trace data + TInt err = file.Open(aFs, aFilePath, EFileRead|EFileShareAny); + if (err != KErrNone) + { + return err; + } + CleanupClosePushL(file); + + RBuf8 fileBuffer; + TInt filePos = 0; + + // Create file buffer and read first chunk from file + err = CreateFileBuffer(fileBuffer, file); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + fileBuffer.CleanupClosePushL(); + + TUint8* data = NULL; + TUint8* startOfData = NULL; + TUint8* endOfData = NULL; + + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + CleanupStack::PopAndDestroy(&file); // close file + return err; + } + + TInt currentNumber = 0; + TInt expectedNumber = 1; + TBool gotNumberFromTrace = EFalse; + TBool isDroppedTrace = EFalse; + + TTraceHeaderSettings traceHeaderSettings; + + while(data < endOfData) + { + err = ParseHeader(data, endOfData-data, traceHeaderSettings); + + if (err == KErrOverflow) + { + // We don't have all the trace data, so read next chunk from file + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err == KErrNone) + { + continue; + } + } + if (err != KErrNone) + { + break; + } + + gotNumberFromTrace = EFalse; + + if (traceHeaderSettings.iPrintfTrace && aGroupId == BTrace::ERDebugPrintf) + { + TPtr8 printfString = ReadTracePrintf(data, traceHeaderSettings.iLengthOfPayload); + // See if this is a printf that matches given string + if (!aFindPrintfPattern) + { + err = KErrArgument; + break; + } + else if (printfString.Match(*aFindPrintfPattern) != KErrNotFound) + { + // Get the line number from the string + err = ParseStringForNumber(printfString, *aFindPrintfPattern, currentNumber); + if (err != KErrNone) + { + // Could not extract number from printf, so stop and fail + break; + } + gotNumberFromTrace = ETrue; + } + // See if this is a dropped trace + else if (printfString.Compare(KDroppedTrace()) == 0) + { + // If we get 2 dropped trace notifications in a row, return an error + if (isDroppedTrace) + { + err = KErrGeneral; + break; + } + isDroppedTrace = ETrue; + } + else + { + isDroppedTrace = EFalse; + } + } + else if (!traceHeaderSettings.iPrintfTrace && traceHeaderSettings.iCategory == aGroupId && traceHeaderSettings.iComponentID == aComponentID) + { + // check dropped trace flag + if (traceHeaderSettings.iHeaderFlags & BTrace::EMissingRecord) + { + // If we get 2 dropped trace notifications in a row, return an error + if (isDroppedTrace) + { + err = KErrGeneral; + break; + } + isDroppedTrace = ETrue; + } + else + { + isDroppedTrace = EFalse; + } + + if (traceHeaderSettings.iLengthOfPayload >= 4) + { + currentNumber = (TInt) Swap(ReadUint32FromBuf(data, traceHeaderSettings.iFromTestWriter)); + traceHeaderSettings.iLengthOfPayload -= 4; + gotNumberFromTrace = ETrue; + } + else + { + // Could not get number from trace, so stop and fail + err = KErrGeneral; + break; + } + } + + if (gotNumberFromTrace) + { + if (currentNumber < expectedNumber) + { + // Start of a new sequence, so stop + break; + } + aLastNumberFound = currentNumber; + if (aLastNumberFound != expectedNumber && !isDroppedTrace) + { + // A printf has been missed out with no notification, so stop and fail + err = KErrGeneral; + break; + } + else if (aLastNumberFound == expectedNumber && isDroppedTrace) + { + // A printf hasn't been missed out despite notification, so stop and fail + err = KErrGeneral; + break; + } + + aNumDroppedTraces += (aLastNumberFound - expectedNumber); + expectedNumber = aLastNumberFound + 1; + + if (traceHeaderSettings.iPrintfTrace) + { + isDroppedTrace = EFalse; + } + } + + data += traceHeaderSettings.iLengthOfPayload; //go to next trace + + if (data == endOfData) + { + // We might not have all the trace data, so read next chunk from file + err = ReadNextChunkFromFile(fileBuffer, file, filePos, data, startOfData, endOfData); + if (err != KErrNone) + { + break; + } + } + } + + CleanupStack::PopAndDestroy(&fileBuffer); // close file buffer + CleanupStack::PopAndDestroy(&file); // close file + + return err; + } + +/* + * Get Printf string from single trace data + * + * @param aBuffer Buffer containing the trace data + * @param aPrintfString Output parameter set to the print string in the trace data + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::GetPrintfFromTrace(TDesC8& aBuffer, TDes8& aPrintfString) + { + TUint8* data = (TUint8*) aBuffer.Ptr(); + + TTraceHeaderSettings traceHeaderSettings; + + TInt err = ParseHeader(data, aBuffer.Size(), traceHeaderSettings); + + if (err == KErrNone && traceHeaderSettings.iPrintfTrace) + { + aPrintfString.Copy(ReadTracePrintf(data, traceHeaderSettings.iLengthOfPayload)); + } + + return err; + } + +/* + * Parse trace data for multipart data, returning the number of traces found and total size of trace data. + * The data is expected to contain a sequence of integers, starting at 0 + * + * @param aBuffer Buffer containing the trace data + * @param aGroupId Group ID of traces to parse + * @param aComponentID Component ID of traces to parse + * @param aMultipartDataSize Output parameter set to total size of data in multipart trace + * @param aNumMultipartTraces Output parameter set to number of traces that make up multipart trace + * @return Standard Symbian error code + */ +EXPORT_C TInt TTraceDataParser::ValidateMultipartTraces(TDesC8& aBuffer, + TGroupId aGroupID, + TComponentId aComponentID, + TInt& aMultipartDataSize, + TInt& aNumMultipartTraces) + { + aMultipartDataSize = 0; + aNumMultipartTraces = 0; + + TInt err = KErrNone; + TUint8* data = (TUint8*) aBuffer.Ptr(); + TUint8* endOfData = data + aBuffer.Size(); + TUint8 currentNumber = 0; + TUint8 expectedNumber = 0; + TUint32 expectedMultiPartTraceID = 0; + TBool foundStartOfMultipart = EFalse; + TBool foundEndOfMultipart = EFalse; + TBool isMultipartTrace = EFalse; + + TTraceHeaderSettings traceHeaderSettings; + + while(data < endOfData) + { + err = ParseHeader(data, endOfData-data, traceHeaderSettings); + if (err != KErrNone) + { + return err; + } + + // Only look at traces with correct group ID / component ID + if (traceHeaderSettings.iCategory == aGroupID && traceHeaderSettings.iComponentID == aComponentID) + { + isMultipartTrace = EFalse; + if (traceHeaderSettings.iMultiPartType == BTrace::EMultipartFirst || + traceHeaderSettings.iMultiPartType == BTrace::EMultipartMiddle || + traceHeaderSettings.iMultiPartType == BTrace::EMultipartLast) + { + isMultipartTrace = ETrue; + } + + if (!isMultipartTrace) + { + // If not a multipart trace, then this one trace should contain all data + aNumMultipartTraces = 1; + aMultipartDataSize = traceHeaderSettings.iLengthOfPayload; + // If already found some of the data then we shouldn't have this trace + if (foundStartOfMultipart || foundEndOfMultipart) + { + return KErrCorrupt; + } + foundStartOfMultipart = ETrue; + foundEndOfMultipart = ETrue; + + // Validate payload + while (traceHeaderSettings.iLengthOfPayload > 0) + { + currentNumber = (TInt) data[0]; + data++; + // Adjust length of payload remaining + traceHeaderSettings.iLengthOfPayload -= 1; + if (currentNumber != expectedNumber) + { + if (traceHeaderSettings.iLengthOfPayload >= 4 || currentNumber != 0) + { + // Data is 4-byte aligned, so it's ok to have up to 3 zeros at the end of payload + return KErrCorrupt; + } + // This byte is zero padding, so not part of data + aMultipartDataSize--; + } + expectedNumber++; + } + } + // Only look at multipart traces with correct multipart trace ID + else if (traceHeaderSettings.iMultiPartTraceID == expectedMultiPartTraceID || expectedMultiPartTraceID == 0) + { + aNumMultipartTraces++; + + // If already found all data then we shouldn't get another trace with this ID + if (foundEndOfMultipart) + { + return KErrCorrupt; + } + + // Trace is start of multipart + if (traceHeaderSettings.iMultiPartType == BTrace::EMultipartFirst) + { + // If already found start of data then we shouldn't get this part + if (foundStartOfMultipart) + { + return KErrCorrupt; + } + foundStartOfMultipart = ETrue; + aMultipartDataSize = 0; + + // Set the expected multipart trace ID + expectedMultiPartTraceID = traceHeaderSettings.iMultiPartTraceID; + } + else if (traceHeaderSettings.iMultiPartType == BTrace::EMultipartMiddle || + traceHeaderSettings.iMultiPartType == BTrace::EMultipartLast) + { + // If not yet found start of data then we shouldn't get this part + if (!foundStartOfMultipart) + { + return KErrCorrupt; + } + } + + // Add length of payload to size of total data found + aMultipartDataSize += traceHeaderSettings.iLengthOfPayload; + + while (traceHeaderSettings.iLengthOfPayload > 0) + { + // Validate the next part of payload + currentNumber = (TInt) data[0]; + data++; + // Adjust length of payload remaining + traceHeaderSettings.iLengthOfPayload -= 1; + if (currentNumber != expectedNumber) + { + if (traceHeaderSettings.iLengthOfPayload >= 4 || currentNumber != 0) + { + // Data is 4-byte aligned, so it's ok to have up to 3 zeros at the end of payload + return KErrCorrupt; + } + // This byte is zero padding, so not part of data + aMultipartDataSize--; + } + expectedNumber++; + } + + if (traceHeaderSettings.iMultiPartType == BTrace::EMultipartLast) + { + // Found end of trace data + foundEndOfMultipart = ETrue; + } + } + } + + data += traceHeaderSettings.iLengthOfPayload; //go to next trace + } + + if (!foundEndOfMultipart) + { + // Did not find end of trace data, so return KErrNotFound + err = KErrNotFound; + } + return err; + }