libraries/clogger/src/PerformanceCritical.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // PerformanceCritical.cpp
       
     2 // 
       
     3 // Copyright (c) 2006 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include <fshell/clogger.h>
       
    14 
       
    15 const TInt KChunkSize = 2*1024*1024; // 2MB
       
    16 
       
    17 #ifdef __ARMCC__
       
    18 // RVCT doesn't like inlining DoLog when it's used more than once
       
    19 #define FORCEINLINE __forceinline
       
    20 #else
       
    21 #define FORCEINLINE 
       
    22 #endif
       
    23 
       
    24 // We use a minimum of 4KB by having any global data, so no need to scrimp or only stash a pointer
       
    25 
       
    26 TBuf8<2048> gTempBuf;
       
    27 TUint8* gTempBufPtr = NULL;
       
    28 
       
    29 RClogger gClogger;
       
    30 RChunk gChunk;
       
    31 TInt* gBufSize = NULL;
       
    32 TUint8* gBufPtr = NULL;
       
    33 
       
    34 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
       
    35 TBuf8<10> gTimeBuf;
       
    36 TUint8* gTimeBufPtr = NULL;
       
    37 
       
    38 enum TPanic
       
    39 	{
       
    40 	E16BitFunctionsNotSupported,
       
    41 	ENotEnoughMemoryForBuffer,
       
    42 	ECouldntConnectToClogger,
       
    43 	EBufferFull,
       
    44 	};
       
    45 
       
    46 void BlowUp(TPanic aPanic)
       
    47 	{
       
    48 	User::Panic(_L("CloggerPerfLog"), aPanic);
       
    49 	}
       
    50 
       
    51 EXPORT_C TInt RClogger::StaticConnect(const TDesC& aTag)
       
    52 	{
       
    53 	if (gInitialised) return KErrNone;
       
    54 
       
    55 	// Will do these because I not 100% certain about what guarantees the runtime provides
       
    56 	// about initialisation of non-POD globals
       
    57 	new(&gClogger) RClogger;
       
    58 	new(&gChunk) RChunk;
       
    59 	new(&gTempBuf) TBuf8<2048>;
       
    60 	new(&gTimeBuf) TBuf8<10>;
       
    61 	gTimeBufPtr = (TUint8*)gTimeBuf.Ptr();
       
    62 	gTempBufPtr = (TUint8*)gTempBuf.Ptr();
       
    63 		
       
    64 	TInt err = gClogger.Connect(aTag);
       
    65 	if (err)
       
    66 		{
       
    67 		BlowUp(ECouldntConnectToClogger);
       
    68 		//gChunk.Close();
       
    69 		}
       
    70 	
       
    71 	TAny* arg0 = &gChunk;
       
    72 	err = gClogger.Reserved(0xC0FFEE, arg0, (TAny*)KChunkSize); // Get chunk from the server using the magic Reserved function
       
    73 	
       
    74 	if (err)
       
    75 		{
       
    76 		BlowUp(ENotEnoughMemoryForBuffer);
       
    77 		}
       
    78 
       
    79 	TUint8* base = gChunk.Base();
       
    80 	gBufSize = reinterpret_cast<TInt*>(base); // Use first word of chunk to indicate size
       
    81 	*gBufSize = 0;
       
    82 	//gBuf.Set(base+4, 0, KChunkSize-4);
       
    83 	gBufPtr = base+4;
       
    84 
       
    85 #ifdef _DEBUG
       
    86 	// For debugging when something failed to write to a part of the chunk
       
    87 	memset(gBufPtr, 'i', KChunkSize-4);
       
    88 #endif
       
    89 
       
    90 	gInitialised = ETrue;
       
    91 	return err;
       
    92 	}
       
    93 
       
    94 EXPORT_C void RClogger::StaticClose()
       
    95 	{
       
    96 	if (gInitialised)
       
    97 		{
       
    98 		gClogger.Close();
       
    99 		gChunk.Close();
       
   100 		gInitialised = EFalse;
       
   101 		}
       
   102 	}
       
   103 
       
   104 //#ifdef __BIG_ENDIAN__
       
   105 //const TUint16 KCrLf = 0x0d0a;
       
   106 //#else
       
   107 //const TUint16 KCrLf = 0x0a0d;
       
   108 //#endif
       
   109 
       
   110 // Use this lookup table as a quick way of hex formatting a string
       
   111 char const * const KHexify = "0123456789ABCDEF";
       
   112 
       
   113 inline void Hexify4(TUint8* aBuf, TUint32 aVal)
       
   114 	{
       
   115 	char const * const hexify = KHexify; // Saves compiler re-reading global
       
   116 	TUint32 idx;
       
   117 	idx = aVal & 0xF;
       
   118 	aBuf[3] = hexify[idx];
       
   119 	idx = (aVal >> 4) & 0xF;
       
   120 	aBuf[2] = hexify[idx];
       
   121 	idx = (aVal >> 8) & 0xF;
       
   122 	aBuf[1] = hexify[idx];
       
   123 	idx = (aVal >> 12) & 0xF;
       
   124 	aBuf[0] = hexify[idx];
       
   125 	}
       
   126 
       
   127 void Hexify8(TUint8* aBuf, TUint32 aVal)
       
   128 	{
       
   129 	// Don't use the global object, it's slightly quicker to access a local variable
       
   130 	// If we were going to forceinline this function it would probably be advisable to go back to using the global one
       
   131 	char const * const hexify = "0123456789ABCDEF";
       
   132 	TUint32 idx;
       
   133 	idx = aVal & 0xF;
       
   134 	aBuf[7] = hexify[idx];
       
   135 	idx = (aVal >> 4) & 0xF;
       
   136 	aBuf[6] = hexify[idx];
       
   137 	idx = (aVal >> 8) & 0xF;
       
   138 	aBuf[5] = hexify[idx];
       
   139 	idx = (aVal >> 12) & 0xF;
       
   140 	aBuf[4] = hexify[idx];
       
   141 	idx = (aVal >> 16) & 0xF;
       
   142 	aBuf[3] = hexify[idx];
       
   143 	idx = (aVal >> 20) & 0xF;
       
   144 	aBuf[2] = hexify[idx];
       
   145 	idx = (aVal >> 24) & 0xF;
       
   146 	aBuf[1] = hexify[idx];
       
   147 	idx = (aVal >> 28) & 0xF;
       
   148 	aBuf[0] = hexify[idx];
       
   149 	}
       
   150 
       
   151 FORCEINLINE void DoLog(TRefByValue<const TDesC8> aFmt, VA_LIST aList)
       
   152 	{
       
   153 	TDes8& tempBuf(gTempBuf);
       
   154 	tempBuf.FormatList(aFmt, aList);
       
   155 	//__ASSERT_ALWAYS(memBuf.MaxLength()-memBuf.Length() >= tempBuf.Length() + 8 + 3, BlowUp(EBufferFull)); // 8 for timebuf, 3 for the space and newlines
       
   156 	// This costs 10 instructions - not much compared to FormatList, but what's the point of checking then panicking anyway?
       
   157 		
       
   158 	TUint8* ptr = gBufPtr;
       
   159 	// Copy timebuf plus a space
       
   160 	//gTimeBuf.NumFixedWidthUC(User::FastCounter(), EHex, 8);
       
   161 	//memcpy(ptr, gTimeBufPtr, 8);
       
   162 	Hexify8(ptr, User::NTickCount());
       
   163 	ptr[8] = ' ';
       
   164 	ptr += 9; // for the space too
       
   165 
       
   166 	// And append the formatted string plus a newline
       
   167 	TInt formatLen = tempBuf.Length();
       
   168 	memcpy(ptr, gTempBufPtr, formatLen);
       
   169 	ptr += formatLen;
       
   170 	//*((TUint16*)ptr) = KCrLf;
       
   171 	// ^ Can't do the above because of alignment issues on ARM
       
   172 	ptr[0] = '\r';
       
   173 	ptr[1] = '\n';
       
   174 	gBufPtr = ptr + 2;
       
   175 	*gBufSize += formatLen + 11; // 8 for timeBuf, 3 for space&CRLF
       
   176 	}
       
   177 
       
   178 EXPORT_C void RClogger::Slog(TUint32 /*aLogMask*/, TRefByValue<const TDesC8> aFmt, ...)
       
   179 	{
       
   180 	VA_LIST args;
       
   181 	VA_START(args, aFmt);
       
   182 	DoLog(aFmt, args);
       
   183 	}
       
   184 
       
   185 EXPORT_C void RClogger::Slog(TRefByValue<const TDesC8> aFmt, ...)
       
   186 	{
       
   187 	VA_LIST args;
       
   188 	VA_START(args, aFmt);
       
   189 	DoLog(aFmt, args);
       
   190 	}
       
   191 
       
   192 EXPORT_C void RClogger::Slog(const char* aFmt, ...)
       
   193 	{
       
   194 	VA_LIST args;
       
   195 	VA_START(args, aFmt);
       
   196 	TPtrC8 ptr((const TUint8*)aFmt);
       
   197 	DoLog(ptr, args);
       
   198 	}
       
   199 
       
   200 EXPORT_C void RClogger::Slog(TUint32 /*aLogMask*/, const char* aFmt, ...)
       
   201 	{
       
   202 	VA_LIST args;
       
   203 	VA_START(args, aFmt);
       
   204 	TPtrC8 ptr((const TUint8*)aFmt);
       
   205 	DoLog(ptr, args);
       
   206 	}
       
   207 
       
   208 EXPORT_C void RClogger::SlogList(TRefByValue<const TDesC8> aFmt, VA_LIST aList)
       
   209 	{
       
   210 	DoLog(aFmt, aList);
       
   211 	}
       
   212 
       
   213 EXPORT_C void RClogger::SlogList(const char* aFmt, VA_LIST aList)
       
   214 	{
       
   215 	TPtrC8 ptr((const TUint8*)aFmt);
       
   216 	DoLog(ptr, aList);
       
   217 	}
       
   218 
       
   219 EXPORT_C void RClogger::SlogList(TUint32 /*aLogMask*/, TRefByValue<const TDesC8> aFmt, VA_LIST aList)
       
   220 	{
       
   221 	DoLog(aFmt, aList);
       
   222 	}
       
   223 
       
   224 EXPORT_C void RClogger::SlogList(TUint32 /*aLogMask*/, const char* aFmt, VA_LIST aList)
       
   225 	{
       
   226 	TPtrC8 ptr((const TUint8*)aFmt);
       
   227 	DoLog(ptr, aList);
       
   228 	}
       
   229 
       
   230 // Screw the 16-bit versions, if you want performance you should be logging in 8-bit
       
   231 EXPORT_C void RClogger::Slog(TRefByValue<const TDesC>, ...) { BlowUp(E16BitFunctionsNotSupported); }
       
   232 EXPORT_C void RClogger::Slog(TUint32, TRefByValue<const TDesC>, ...) { BlowUp(E16BitFunctionsNotSupported); }
       
   233 EXPORT_C void RClogger::SlogList(TRefByValue<const TDesC>, VA_LIST) { BlowUp(E16BitFunctionsNotSupported); }
       
   234 EXPORT_C void RClogger::SlogList(TUint32, TRefByValue<const TDesC>, VA_LIST) { BlowUp(E16BitFunctionsNotSupported); }
       
   235 
       
   236 // Doesn't need to do anything
       
   237 EXPORT_C void RClogger::SetStaticLogBehaviour(TUint /*aLogBehaviour*/)
       
   238 	{
       
   239 	}
       
   240 
       
   241 /*
       
   242 
       
   243 Actually, the compiler's may not be optimal but it uses one less register, so avoids accessing main mem as much!
       
   244 
       
   245 __NAKED__ void Hexify4(TUint8* aBuf, TUint32 aVal, TUint8 const*const aHexify)
       
   246 	{
       
   247 	// Compiler refuses to generate optimal assembly, so screw it
       
   248 
       
   249 	// r0 is aBuf, r1 is aVal, r2 is hexify
       
   250 	// r4 is 0xF for convenience of ANDing
       
   251 	asm("push {r4}");
       
   252 	asm("mov r4, #0xF");
       
   253 
       
   254 	asm("and r3, r4, r1"); // r3 = aVal & 0xF
       
   255 	asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
       
   256 	asm("strb r3, [r0, #3]"); // aBuf[3] = r3
       
   257 
       
   258 	asm("and r3, r4, r1, lsr #4"); // r3 = 0xF & (aVal >> 4)
       
   259 	asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
       
   260 	asm("strb r3, [r0, #2]"); // aBuf[2] = r3
       
   261 	
       
   262 	asm("and r3, r4, r1, lsr #8"); // r3 = 0xF & (aVal >> 8)
       
   263 	asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
       
   264 	asm("strb r3, [r0, #1]"); // aBuf[1] = r3
       
   265 
       
   266 	asm("and r3, r4, r1, lsr #16"); // r3 = 0xF & (aVal >> 16)
       
   267 	asm("ldrb r3, [r2, r3]"); // r3 = aHexify[r3]
       
   268 	asm("strb r3, [r0, #0]"); // aBuf[0] = r3
       
   269 
       
   270 	asm("pop {r4}");
       
   271 	asm("bx lr");
       
   272 	}
       
   273 */
       
   274 		
       
   275 FORCEINLINE void DoHexDump(const TDesC8& aHeader, const TDesC8& aData)
       
   276 	{
       
   277 	//__DEBUGGER();
       
   278 	TUint8 * const origBufPtr = gBufPtr;
       
   279 
       
   280 	const TInt KLineSize = 16;
       
   281 	const TInt KExtra = 19; // 8-digit timestamp, space, 4-digit idx, space colon space, space, CRLF
       
   282 	TUint32 tickCount = User::NTickCount();
       
   283 	TUint8 timestampBuf[8];
       
   284 	//TPtr8(timestampBuf, 8, 8).NumFixedWidthUC(tickCount, EHex, 8); // TODO replace with custom hexifier?
       
   285 	Hexify8(timestampBuf, tickCount);
       
   286 
       
   287 	TUint8 const * dataPtr = aData.Ptr();
       
   288 	TUint8 const * const dataEnd = dataPtr + aData.Length();
       
   289 	TInt headerLen = aHeader.Length();
       
   290 	// 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
       
   291 	memset(origBufPtr, ' ', (headerLen + 4*KLineSize + KExtra)*(aData.Length()+8)/16); // I think this is linelen*numLines
       
   292 
       
   293 	// Do "Timestamp Header : "
       
   294 	memcpy(origBufPtr, timestampBuf, 8);
       
   295 	TUint8* bufPtr = origBufPtr + 9;
       
   296 	memcpy(bufPtr, aHeader.Ptr(), headerLen);
       
   297 	bufPtr += headerLen;
       
   298 
       
   299 	TInt offset = 0;
       
   300 	while (dataPtr < dataEnd)
       
   301 		{
       
   302 		const TInt fragLen = Min(KLineSize, dataEnd-dataPtr);
       
   303 		
       
   304 		if (offset)
       
   305 			{
       
   306 			// Write continuation empty space
       
   307 			//bufPtr = memset(bufPtr, ' ', headerLen);
       
   308 			memcpy(bufPtr, timestampBuf, 8);
       
   309 			bufPtr += 9 + headerLen; // 8-byte timestamp, space
       
   310 			}
       
   311 
       
   312 		// Do "0000 : "
       
   313 		//gTempBuf.NumFixedWidthUC(offset++, EHex, 4);
       
   314 		//memcpy(bufPtr, gTempBufPtr, 4);
       
   315 		Hexify4(bufPtr, offset);
       
   316 		offset += 16;
       
   317 
       
   318 		bufPtr += 4;
       
   319 		//bufPtr[0] = ' ';
       
   320 		bufPtr[1] = ':';
       
   321 		//bufPtr[2] = ' ';
       
   322 		bufPtr += 3;
       
   323 
       
   324 		// Do the hexdump of this line's data
       
   325 		TInt i = fragLen;
       
   326 		while(i--)
       
   327 			{
       
   328 			TUint8 c = dataPtr[i];
       
   329 			TInt idx = i*3;
       
   330 			//TInt idx = fragLen<<1 + fragLen;
       
   331 			bufPtr[idx] = KHexify[c>>4];
       
   332 			bufPtr[idx+1] = KHexify[c & 0xF];
       
   333 			//bufPtr[idx+2] = ' ';
       
   334 			}
       
   335 		bufPtr += KLineSize*3 + 1; // Plus 1 for the extra space char between the hex rep and the nonhex
       
   336 		memcpy(bufPtr, dataPtr, fragLen);
       
   337 		bufPtr[fragLen] = '\r';
       
   338 		bufPtr[fragLen+1] = '\n';
       
   339 
       
   340 		// Now escape anything nonprintable with '.'
       
   341 		i = fragLen;
       
   342 		while (i--)
       
   343 			{
       
   344 			TUint8 c = bufPtr[i];
       
   345 			if (c < 0x20 || c > 0x7E) bufPtr[i] = '.';
       
   346 			}
       
   347 		bufPtr += fragLen + 2; // For the newline
       
   348 		dataPtr += fragLen;
       
   349 		}
       
   350 	gBufPtr = bufPtr;
       
   351 	*gBufSize += gBufPtr - origBufPtr;
       
   352 	}
       
   353 
       
   354 EXPORT_C void RClogger::StaticHexDump(TUint32 /*aLogMask*/, const TDesC8& aHeader, const TDesC8& aData)
       
   355 	{
       
   356 	//TODO
       
   357 	//gClogger.HexDump(aLogMask, aHeader, aData);
       
   358 	DoHexDump(aHeader, aData);
       
   359 	}
       
   360 
       
   361 EXPORT_C void RClogger::StaticHexDump(const TDesC8& aHeader, const TDesC8& aData)
       
   362 	{
       
   363 	//TODO
       
   364 	//gClogger.HexDump(EAllEnabled, aHeader, aData);
       
   365 	DoHexDump(aHeader, aData);
       
   366 	}