Tidied iocli exports, build macro tweaks.
Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll.
fixed builds on platforms that don't support btrace or any form of tracing.
// PerformanceCritical.cpp
//
// Copyright (c) 2006 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include <fshell/clogger.h>
const TInt KChunkSize = 2*1024*1024; // 2MB
#ifdef __ARMCC__
// RVCT doesn't like inlining DoLog when it's used more than once
#define FORCEINLINE __forceinline
#else
#define FORCEINLINE
#endif
// We use a minimum of 4KB by having any global data, so no need to scrimp or only stash a pointer
TBuf8<2048> gTempBuf;
TUint8* gTempBufPtr = NULL;
RClogger gClogger;
RChunk gChunk;
TInt* gBufSize = NULL;
TUint8* gBufPtr = NULL;
TBool gInitialised = EFalse; // Since POD is guaranteed to be initialised properly we'll use this to test whether we've already setup the global data
TBuf8<10> gTimeBuf;
TUint8* gTimeBufPtr = NULL;
enum TPanic
{
E16BitFunctionsNotSupported,
ENotEnoughMemoryForBuffer,
ECouldntConnectToClogger,
EBufferFull,
};
void BlowUp(TPanic aPanic)
{
User::Panic(_L("CloggerPerfLog"), aPanic);
}
EXPORT_C TInt RClogger::StaticConnect(const TDesC& aTag)
{
if (gInitialised) return KErrNone;
// Will do these because I not 100% certain about what guarantees the runtime provides
// about initialisation of non-POD globals
new(&gClogger) RClogger;
new(&gChunk) RChunk;
new(&gTempBuf) TBuf8<2048>;
new(&gTimeBuf) TBuf8<10>;
gTimeBufPtr = (TUint8*)gTimeBuf.Ptr();
gTempBufPtr = (TUint8*)gTempBuf.Ptr();
TInt err = gClogger.Connect(aTag);
if (err)
{
BlowUp(ECouldntConnectToClogger);
//gChunk.Close();
}
TAny* arg0 = &gChunk;
err = gClogger.Reserved(0xC0FFEE, arg0, (TAny*)KChunkSize); // Get chunk from the server using the magic Reserved function
if (err)
{
BlowUp(ENotEnoughMemoryForBuffer);
}
TUint8* base = gChunk.Base();
gBufSize = reinterpret_cast<TInt*>(base); // Use first word of chunk to indicate size
*gBufSize = 0;
//gBuf.Set(base+4, 0, KChunkSize-4);
gBufPtr = base+4;
#ifdef _DEBUG
// For debugging when something failed to write to a part of the chunk
memset(gBufPtr, 'i', KChunkSize-4);
#endif
gInitialised = ETrue;
return err;
}
EXPORT_C void RClogger::StaticClose()
{
if (gInitialised)
{
gClogger.Close();
gChunk.Close();
gInitialised = EFalse;
}
}
//#ifdef __BIG_ENDIAN__
//const TUint16 KCrLf = 0x0d0a;
//#else
//const TUint16 KCrLf = 0x0a0d;
//#endif
// Use this lookup table as a quick way of hex formatting a string
char const * const KHexify = "0123456789ABCDEF";
inline void Hexify4(TUint8* aBuf, TUint32 aVal)
{
char const * const hexify = KHexify; // Saves compiler re-reading global
TUint32 idx;
idx = aVal & 0xF;
aBuf[3] = hexify[idx];
idx = (aVal >> 4) & 0xF;
aBuf[2] = hexify[idx];
idx = (aVal >> 8) & 0xF;
aBuf[1] = hexify[idx];
idx = (aVal >> 12) & 0xF;
aBuf[0] = hexify[idx];
}
void Hexify8(TUint8* aBuf, TUint32 aVal)
{
// Don't use the global object, it's slightly quicker to access a local variable
// If we were going to forceinline this function it would probably be advisable to go back to using the global one
char const * const hexify = "0123456789ABCDEF";
TUint32 idx;
idx = aVal & 0xF;
aBuf[7] = hexify[idx];
idx = (aVal >> 4) & 0xF;
aBuf[6] = hexify[idx];
idx = (aVal >> 8) & 0xF;
aBuf[5] = hexify[idx];
idx = (aVal >> 12) & 0xF;
aBuf[4] = hexify[idx];
idx = (aVal >> 16) & 0xF;
aBuf[3] = hexify[idx];
idx = (aVal >> 20) & 0xF;
aBuf[2] = hexify[idx];
idx = (aVal >> 24) & 0xF;
aBuf[1] = hexify[idx];
idx = (aVal >> 28) & 0xF;
aBuf[0] = hexify[idx];
}
FORCEINLINE void DoLog(TRefByValue<const TDesC8> aFmt, VA_LIST aList)
{
TDes8& tempBuf(gTempBuf);
tempBuf.FormatList(aFmt, aList);
//__ASSERT_ALWAYS(memBuf.MaxLength()-memBuf.Length() >= tempBuf.Length() + 8 + 3, BlowUp(EBufferFull)); // 8 for timebuf, 3 for the space and newlines
// This costs 10 instructions - not much compared to FormatList, but what's the point of checking then panicking anyway?
TUint8* ptr = gBufPtr;
// Copy timebuf plus a space
//gTimeBuf.NumFixedWidthUC(User::FastCounter(), EHex, 8);
//memcpy(ptr, gTimeBufPtr, 8);
Hexify8(ptr, User::NTickCount());
ptr[8] = ' ';
ptr += 9; // for the space too
// And append the formatted string plus a newline
TInt formatLen = tempBuf.Length();
memcpy(ptr, gTempBufPtr, formatLen);
ptr += formatLen;
//*((TUint16*)ptr) = KCrLf;
// ^ Can't do the above because of alignment issues on ARM
ptr[0] = '\r';
ptr[1] = '\n';
gBufPtr = ptr + 2;
*gBufSize += formatLen + 11; // 8 for timeBuf, 3 for space&CRLF
}
EXPORT_C void RClogger::Slog(TUint32 /*aLogMask*/, TRefByValue<const TDesC8> aFmt, ...)
{
VA_LIST args;
VA_START(args, aFmt);
DoLog(aFmt, args);
}
EXPORT_C void RClogger::Slog(TRefByValue<const TDesC8> aFmt, ...)
{
VA_LIST args;
VA_START(args, aFmt);
DoLog(aFmt, args);
}
EXPORT_C void RClogger::Slog(const char* aFmt, ...)
{
VA_LIST args;
VA_START(args, aFmt);
TPtrC8 ptr((const TUint8*)aFmt);
DoLog(ptr, args);
}
EXPORT_C void RClogger::Slog(TUint32 /*aLogMask*/, const char* aFmt, ...)
{
VA_LIST args;
VA_START(args, aFmt);
TPtrC8 ptr((const TUint8*)aFmt);
DoLog(ptr, args);
}
EXPORT_C void RClogger::SlogList(TRefByValue<const TDesC8> aFmt, VA_LIST aList)
{
DoLog(aFmt, aList);
}
EXPORT_C void RClogger::SlogList(const char* aFmt, VA_LIST aList)
{
TPtrC8 ptr((const TUint8*)aFmt);
DoLog(ptr, aList);
}
EXPORT_C void RClogger::SlogList(TUint32 /*aLogMask*/, TRefByValue<const TDesC8> aFmt, VA_LIST aList)
{
DoLog(aFmt, aList);
}
EXPORT_C void RClogger::SlogList(TUint32 /*aLogMask*/, const char* aFmt, VA_LIST aList)
{
TPtrC8 ptr((const TUint8*)aFmt);
DoLog(ptr, aList);
}
// Screw the 16-bit versions, if you want performance you should be logging in 8-bit
EXPORT_C void RClogger::Slog(TRefByValue<const TDesC>, ...) { BlowUp(E16BitFunctionsNotSupported); }
EXPORT_C void RClogger::Slog(TUint32, TRefByValue<const TDesC>, ...) { BlowUp(E16BitFunctionsNotSupported); }
EXPORT_C void RClogger::SlogList(TRefByValue<const TDesC>, VA_LIST) { BlowUp(E16BitFunctionsNotSupported); }
EXPORT_C void RClogger::SlogList(TUint32, TRefByValue<const TDesC>, VA_LIST) { BlowUp(E16BitFunctionsNotSupported); }
// Doesn't need to do anything
EXPORT_C void RClogger::SetStaticLogBehaviour(TUint /*aLogBehaviour*/)
{
}
/*
Actually, the compiler's may not be optimal but it uses one less register, so avoids accessing main mem as much!
__NAKED__ void Hexify4(TUint8* aBuf, TUint32 aVal, TUint8 const*const aHexify)
{
// Compiler refuses to generate optimal assembly, so screw it
// r0 is aBuf, r1 is aVal, r2 is hexify
// r4 is 0xF for convenience of ANDing
asm("push {r4}");
asm("mov r4, #0xF");
asm("and r3, r4, r1"); // r3 = aVal & 0xF
asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
asm("strb r3, [r0, #3]"); // aBuf[3] = r3
asm("and r3, r4, r1, lsr #4"); // r3 = 0xF & (aVal >> 4)
asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
asm("strb r3, [r0, #2]"); // aBuf[2] = r3
asm("and r3, r4, r1, lsr #8"); // r3 = 0xF & (aVal >> 8)
asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
asm("strb r3, [r0, #1]"); // aBuf[1] = r3
asm("and r3, r4, r1, lsr #16"); // r3 = 0xF & (aVal >> 16)
asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
asm("strb r3, [r0, #0]"); // aBuf[0] = r3
asm("pop {r4}");
asm("bx lr");
}
*/
FORCEINLINE void DoHexDump(const TDesC8& aHeader, const TDesC8& aData)
{
//__DEBUGGER();
TUint8 * const origBufPtr = gBufPtr;
const TInt KLineSize = 16;
const TInt KExtra = 19; // 8-digit timestamp, space, 4-digit idx, space colon space, space, CRLF
TUint32 tickCount = User::NTickCount();
TUint8 timestampBuf[8];
//TPtr8(timestampBuf, 8, 8).NumFixedWidthUC(tickCount, EHex, 8); // TODO replace with custom hexifier?
Hexify8(timestampBuf, tickCount);
TUint8 const * dataPtr = aData.Ptr();
TUint8 const * const dataEnd = dataPtr + aData.Length();
TInt headerLen = aHeader.Length();
// Rather than messing around with putting spaces in where needed, fill the whole buffer with spaces. It'll probably be more efficient to do one big fill like this
memset(origBufPtr, ' ', (headerLen + 4*KLineSize + KExtra)*(aData.Length()+8)/16); // I think this is linelen*numLines
// Do "Timestamp Header : "
memcpy(origBufPtr, timestampBuf, 8);
TUint8* bufPtr = origBufPtr + 9;
memcpy(bufPtr, aHeader.Ptr(), headerLen);
bufPtr += headerLen;
TInt offset = 0;
while (dataPtr < dataEnd)
{
const TInt fragLen = Min(KLineSize, dataEnd-dataPtr);
if (offset)
{
// Write continuation empty space
//bufPtr = memset(bufPtr, ' ', headerLen);
memcpy(bufPtr, timestampBuf, 8);
bufPtr += 9 + headerLen; // 8-byte timestamp, space
}
// Do "0000 : "
//gTempBuf.NumFixedWidthUC(offset++, EHex, 4);
//memcpy(bufPtr, gTempBufPtr, 4);
Hexify4(bufPtr, offset);
offset += 16;
bufPtr += 4;
//bufPtr[0] = ' ';
bufPtr[1] = ':';
//bufPtr[2] = ' ';
bufPtr += 3;
// Do the hexdump of this line's data
TInt i = fragLen;
while(i--)
{
TUint8 c = dataPtr[i];
TInt idx = i*3;
//TInt idx = fragLen<<1 + fragLen;
bufPtr[idx] = KHexify[c>>4];
bufPtr[idx+1] = KHexify[c & 0xF];
//bufPtr[idx+2] = ' ';
}
bufPtr += KLineSize*3 + 1; // Plus 1 for the extra space char between the hex rep and the nonhex
memcpy(bufPtr, dataPtr, fragLen);
bufPtr[fragLen] = '\r';
bufPtr[fragLen+1] = '\n';
// Now escape anything nonprintable with '.'
i = fragLen;
while (i--)
{
TUint8 c = bufPtr[i];
if (c < 0x20 || c > 0x7E) bufPtr[i] = '.';
}
bufPtr += fragLen + 2; // For the newline
dataPtr += fragLen;
}
gBufPtr = bufPtr;
*gBufSize += gBufPtr - origBufPtr;
}
EXPORT_C void RClogger::StaticHexDump(TUint32 /*aLogMask*/, const TDesC8& aHeader, const TDesC8& aData)
{
//TODO
//gClogger.HexDump(aLogMask, aHeader, aData);
DoHexDump(aHeader, aData);
}
EXPORT_C void RClogger::StaticHexDump(const TDesC8& aHeader, const TDesC8& aData)
{
//TODO
//gClogger.HexDump(EAllEnabled, aHeader, aData);
DoHexDump(aHeader, aData);
}