// 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);
}