diff -r 000000000000 -r a41df078684a kerneltest/e32test/nkernsa/kprintf.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/nkernsa/kprintf.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,788 @@ +// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32test\nkernsa\kprintf.cpp +// +// + +#define __E32CMN_H__ +#include +#include "nk_priv.h" + +#undef EXPORT_C +#define EXPORT_C /* */ + +class TVersion + { +public: + TInt8 iMajor; + TInt8 iMinor; + TInt16 iBuild; + }; + +class TDesC; + +#include + +extern void DebugPrint(const char*, int); + +/** +Returns the active debug mask obtained by logically ANDing the global debug mask +in the super page with the per-thread debug mask in the current DThread object. + +If the current thread is not a symbian OS thread the global debug mask is used. + +Only supports the first 32 global debug trace bits. + +@return The debug mask. +*/ +extern "C" { +extern TLinAddr RomHeaderAddress; +} +EXPORT_C TInt KDebugMask() + { + const TRomHeader& rh = *(const TRomHeader*)RomHeaderAddress; + return rh.iTraceMask[0]; + } + + + +/** +Returns the state (ETrue or EFalse) of given bit in the active debug mask +which is obtained by logically ANDing the global debug mask in the super page +with the per-thread debug mask in the current DThread object. + +If the current thread is not a symbian OS thread the global debug mask is used. + +@return The state of the debug mask bit number. +*/ + +EXPORT_C TBool KDebugNum(TInt aBitNum) + { +#if 1 + const TRomHeader& rh = *(const TRomHeader*)RomHeaderAddress; + TInt m = 0; + + // special case for KALWAYS + if (aBitNum == KALWAYS) + { + m = rh.iTraceMask[0] || + rh.iTraceMask[1] || + rh.iTraceMask[2] || + rh.iTraceMask[3] || + rh.iTraceMask[4] || + rh.iTraceMask[5] || + rh.iTraceMask[6] || + rh.iTraceMask[7]; + } + else if ( (aBitNum > KMAXTRACE) || (aBitNum < 0) ) + m = 0; + else + { + TInt index = aBitNum >> 5; + m = rh.iTraceMask[index]; + m &= 1 << (aBitNum & 31); + } + + return (m != 0); +#else + return 1; +#endif + } + +extern "C" unsigned int strlen(const char* s) + { + const char* s0 = s; + while(*s++) {} + return s - s0 - 1; + } + +int appendstr(char* out, int outlen, const char* s) + { + if (!s) + s = "NULL"; + char* d = out + outlen; + while(*s) + *d++ = *s++; + return d - out; + } + +int AppendNumBase10U(char* out, unsigned int val, int width, int fill) + { + int len = 10; + if (val < 10) + len = 1; + else if (val < 100) + len = 2; + else if (val < 1000) + len = 3; + else if (val < 10000) + len = 4; + else if (val < 100000) + len = 5; + else if (val < 1000000) + len = 6; + else if (val < 10000000) + len = 7; + else if (val < 100000000) + len = 8; + else if (val < 1000000000) + len = 9; + int w = (len < width) ? width : len; + char* d = out + w; + do { + *--d = (char)(48 + val%10); + val /= 10; + } while(val); + for (; d > out; *--d = (char)fill ) {} + return w; + } + +int AppendNumBase10S(char* out, int sval, int width, int fill) + { + int sign = (sval<0) ? 1 : 0; + unsigned val = sign ? unsigned(-sval) : unsigned(sval); + int len = 10; + if (val < 10) + len = 1; + else if (val < 100) + len = 2; + else if (val < 1000) + len = 3; + else if (val < 10000) + len = 4; + else if (val < 100000) + len = 5; + else if (val < 1000000) + len = 6; + else if (val < 10000000) + len = 7; + else if (val < 100000000) + len = 8; + else if (val < 1000000000) + len = 9; + if (sign) ++len; + int w = (len < width) ? width : len; + char* d = out + w; + do { + *--d = (char)(48 + val%10); + val /= 10; + } while(val); + if (sign) *--d = '-'; + for (; d > out; *--d = (char)fill ) {} + return w; + } + +int AppendNumBase16(char* out, unsigned int val, int width, int fill) + { + int len = 8; + if (val < 0x10) + len = 1; + else if (val < 0x100) + len = 2; + else if (val < 0x1000) + len = 3; + else if (val < 0x10000) + len = 4; + else if (val < 0x100000) + len = 5; + else if (val < 0x1000000) + len = 6; + else if (val < 0x10000000) + len = 7; + int w = (len < width) ? width : len; + char* d = out + w; + do { + char c = (char)(48 + (val&15)); + if (c>'9') c+=0x07; + *--d = c; + val >>= 4; + } while(val); + for (; d > out; *--d = (char)fill ) {} + return w; + } + +int AppendNumBase16L(char* out, Uint64 val, int width, int fill) + { + TUint vl = (TUint)val; + TUint vh = (TUint)(val>>32); + TInt l = 0; + if (vh) + { + l = AppendNumBase16(out, vh, width-8, fill); + l += AppendNumBase16(out+l, vl, 8, fill); + } + else + l = AppendNumBase16(out, vl, width, fill); + return l; + } + + + +/** +Formats and appends text to the specified narrow descriptor without making any +executive calls. + +The function takes a format string and a variable number of arguments. The +format specifiers in the format string are used to interpret and the arguments. + +Format directives have the following syntax: +@code + ::= + "%" [] [] [] +@endcode + +If a field width is specified and the width of the formatted field is less +than this width, then the field is padded with the padding character. +The only supported padding characters are ' ' (default) and '0'. + +The long flag specifier ('l') modifies the semantic of the conversion +specifier as explained below. + +The possible values for the conversion specifiers, the long flag and the way in +which arguments are interpreted, are as follows: +@code +d Interpret the argument as a TInt decimal representation +ld NOT SUPPORTED - use lx instead +u Interpret the argument as a TUint decimal representation +lu NOT SUPPORTED - use lx instead +x Interpret the argument as a TUint hexadecimal representation +X As above +lx Interpret the argument as a Uint64 hexadecimal representation +lX As above +c Interpret the argument as a character +s Interpret the argument as a pointer to narrow C string +ls Interpret the argument as a pointer to narrow C string +S Interpret the argument as a pointer to narrow descriptor or NULL +lS NOT SUPPORTED - use S instead +O Interpret the argument as a pointer to DObject or NULL + Generates the object full name or 'NULL' +o Interpret the argument as a pointer to DObject or NULL + Generates the object name or 'NULL' +M Interpret the argument as a pointer to a fast mutex or NULL + Generates the name, if this is a well-known fast mutex, address otherwise +m Interpret the argument as a pointer to a fast semaphore or NULL + Generates the owning thread name, if this is a well-known fast semaphore, address otherwise +T Interpret the argument as a pointer to a nanothread or NULL + Generates the full name, if this is a Symbian OS thread, address otherwise +C Interpret the argument as a pointer to a DCodeSeg or NULL + Generates the filename and module version number +@endcode + +The function can be called from the interrupt context, but extreme caution is advised as it +may require a lot of stack space and interrupt stacks are very small. + +@param aDes Narrow descriptor that must be big-enough to hold result +@param aFmt The format string +@param aList A variable number of arguments to be converted to text as dictated by the format string + +@pre Calling thread can be either in a critical section or not. +@pre Interrupts must be enabled. +@pre Kernel must be unlocked +@pre No fast mutex can be held. +@pre Call in any context. +@pre Suitable for use in a device driver + +@panic The set of panics that can be raised when appending data to descriptors. + +@see TDes8 +*/ +EXPORT_C TInt AppendFormat(char* aOut, const char* aFmt, VA_LIST aList) + { +#define NEXT_FMT(c,p) if (((c)=*(p)++)==0) return outLen; + + TInt outLen = 0; + FOREVER + { + char c; + NEXT_FMT(c,aFmt); + if (c=='%') + { + char fill=' '; + TInt width=0; + TBool long_arg=EFalse; + TBool ok=ETrue; + NEXT_FMT(c,aFmt); + if (c=='0') + { + fill='0'; + NEXT_FMT(c,aFmt); + } + while(c>='0' && c<='9') + { + width=width*10+c-'0'; + NEXT_FMT(c,aFmt); + } + if (c=='l') + { + long_arg=ETrue; + NEXT_FMT(c,aFmt); + } + switch(c) + { + case 'd': + { + if (long_arg) + ok=EFalse; + else + { + TInt val=VA_ARG(aList,TInt); + char* d = aOut + outLen; + outLen += AppendNumBase10S(d, val, width, fill); + } + break; + } + case 'u': + { + if (long_arg) + ok=EFalse; + else + { + TUint val=VA_ARG(aList,TUint); + char* d = aOut + outLen; + outLen += AppendNumBase10U(d, val, width, fill); + } + break; + } + case 'x': + case 'X': + { + if (long_arg) + { + Uint64 val=VA_ARG(aList,Uint64); + char* d = aOut + outLen; + outLen += AppendNumBase16L(d, val, width, fill); + } + else + { + TUint val=VA_ARG(aList,TUint); + char* d = aOut + outLen; + outLen += AppendNumBase16(d, val, width, fill); + } + break; + } + case 's': + { + const char* s = VA_ARG(aList,const char*); + outLen = appendstr(aOut, outLen, s); + break; + } + case 'M': // fast mutex + { + NFastMutex* pM=VA_ARG(aList,NFastMutex*); + outLen = appendstr(aOut, outLen, "M"); + if (!pM) + outLen = appendstr(aOut, outLen, 0); + else if (pM==&TheScheduler.iLock) + outLen = appendstr(aOut, outLen, "SYSLOCK"); + else + outLen += AppendNumBase16(aOut+outLen, (TUint)pM, 8, '0'); + break; + } + case 'm': // fast semaphore + { + NFastSemaphore* pS=VA_ARG(aList,NFastSemaphore*); + outLen = appendstr(aOut, outLen, "S"); + if (!pS) + outLen = appendstr(aOut, outLen, 0); + else + outLen += AppendNumBase16(aOut+outLen, (TUint)pS, 8, '0'); + break; + } + case 'T': // NKERN thread + { + NThread* pN=VA_ARG(aList,NThread*); +#ifdef __SMP__ + if (pN && pN->iNThreadBaseSpare8) + outLen = appendstr(aOut, outLen, (const char*)pN->iNThreadBaseSpare8); +#else + if (pN && pN->iSpare8) + outLen = appendstr(aOut, outLen, (const char*)pN->iSpare8); +#endif + else + { + outLen = appendstr(aOut, outLen, "T"); + if (!pN) + outLen = appendstr(aOut, outLen, 0); + else + outLen += AppendNumBase16(aOut+outLen, (TUint)pN, 8, '0'); + } + break; + } +#ifdef __SMP__ + case 'G': // NKERN thread group + { + NThreadGroup* pG=VA_ARG(aList,NThreadGroup*); +// if (pG && pG->iNThreadBaseSpare8) +// outLen = appendstr(aOut, outLen, (const char*)pG->iNThreadBaseSpare8); +// else + { + outLen = appendstr(aOut, outLen, "G"); + if (!pG) + outLen = appendstr(aOut, outLen, 0); + else + outLen += AppendNumBase16(aOut+outLen, (TUint)pG, 8, '0'); + } + break; + } +#endif + case 'c': + c=(char)VA_ARG(aList,TUint); + // fall through + default: + ok=EFalse; + break; + } + if (ok) + continue; + } + aOut[outLen++]=c; + } + return outLen; + } + + +/** +Prints a formatted string on the debug port. + +The function uses Kern::AppendFormat() to do the formatting. + +Although it is safe to call this function from an ISR, it polls the output +serial port and may take a long time to complete, invalidating any +real-time guarantee. + +If called from an ISR, it is possible for output text to be intermingled +with other output text if one set of output interrupts or preempts another. + +Some of the formatting options may not work inside an ISR. + +Be careful not to use a string that is too long to fit onto the stack. + +@param aFmt The format string. This must not be longer than 256 characters. +@param ... A variable number of arguments to be converted to text as dictated + by the format string. + +@pre Calling thread can either be in a critical section or not. +@pre Interrupts must be enabled. +@pre Kernel must be unlocked +@pre No fast mutex can be held. +@pre Call in any context. +@pre Suitable for use in a device driver + +@see Kern::AppendFormat() +*/ +extern "C" void puts(const char* s); +extern "C" void prthex8(TUint); +EXPORT_C void KPrintf(const char* aFmt, ...) + { + extern TUint32 __tr(); + + char printBuf[256]; + VA_LIST list; + VA_START(list,aFmt); + int c = AppendFormat(printBuf+2,aFmt,list) + 2; + printBuf[c++] = 13; + printBuf[c++] = 10; + printBuf[0] = __trace_cpu_num()+48; + printBuf[1] = 58; + + if (NKern::Crashed()) + { + DebugPrint(printBuf,c); + return; + } + + // Handle BTrace first... + TUint category = BTrace::EKernPrintf; + TInt result = BTraceContextBig(category,0,0,printBuf,c); + + NThread* csThread = 0; + NThread* curr = NKern::CurrentThread(); + if (curr && NKern::CurrentContext() == NKern::EThread && !NKern::KernelLocked()) + { + csThread = curr; + NKern::_ThreadEnterCS(); + } + if (!result) + { + DebugPrint(printBuf,c); + } + if (csThread) + { + NKern::_ThreadLeaveCS(); + } + } + + + +/****************************************************************************** + * BTRACE SUPPORT + ******************************************************************************/ + +#define BTRACE_INCLUDE_TIMESTAMPS + +TAny* BTraceBufferBase[KMaxCpus]; +TAny* BTraceBufferEnd[KMaxCpus]; +TAny* BTraceBufferPtr[KMaxCpus]; // next free position +TBool BTraceBufferWrap[KMaxCpus]; +TBool BTraceActive; + +//const TUint KBTraceBufferSize = 16 * 1024 * 1024; +const TUint KBTraceBufferSize = 1 * 1024 * 1024; +const TUint KBTraceSlotSize = 128; + +__ASSERT_COMPILE(KBTraceSlotSize >= (TUint)KMaxBTraceRecordSize); + +TBool HandleBTrace(TUint32 aHeader,TUint32 aHeader2,const TUint32 aContext,const TUint32 a1,const TUint32 a2,const TUint32 a3,const TUint32 aExtra,const TUint32 aPc) + { +#ifdef __SMP__ + // Header 2 always present and contains CPU number + // If Header2 not originally there, add 4 to size + TInt cpu = NKern::CurrentCpu(); + if (!(aHeader&(BTrace::EHeader2Present<= BTraceBufferEnd[cpu]) + { + BTraceBufferPtr[cpu] = BTraceBufferBase[cpu]; + BTraceBufferWrap[cpu] = TRUE; + } + + size >>= 2; // we are now counting words, not bytes + + if (dst+size > (TUint32*)BTraceBufferEnd[cpu]) + goto trace_dropped; + + { + // store first word of trace... + TUint w = aHeader; + --size; + *dst++ = w; + +#ifndef __SMP__ + if (aHeader&(BTrace::EHeader2Present<<(BTrace::EFlagsIndex*8))) +#endif + { + w = aHeader2; + --size; + *dst++ = w; + } + +#ifdef BTRACE_INCLUDE_TIMESTAMPS + // store timestamp... +#if defined(__EPOC32__) && defined(__CPU_X86) + --size; + *dst++ = TUint32(timeStamp); + --size; + *dst++ = TUint32(timeStamp>>32); +#else + --size; + *dst++ = timeStamp; +#endif +#endif + + if(aHeader&(BTrace::EContextIdPresent<<(BTrace::EFlagsIndex*8))) + { + w = aContext; + --size; + *dst++ = w; + } + + if(aHeader&(BTrace::EPcPresent<<(BTrace::EFlagsIndex*8))) + { + w = aPc; + --size; + *dst++ = w; + } + + if(aHeader&(BTrace::EExtraPresent<<(BTrace::EFlagsIndex*8))) + { + w = aExtra; + --size; + *dst++ = w; + } + + // store remaining words of trace... + if(size) + { + w = a1; + --size; + *dst++ = w; + if(size) + { + w = a2; + --size; + *dst++ = w; + if(size) + { + if(size==1) + { + w = a3; + *dst++ = w; + } + else + { + src = (TUint32*)a3; + do + { + w = *src++; + --size; + *dst++ = w; + } + while(size); + } + } + } + } + } + +#if !defined(__SMP__) || !defined(__USE_BTRACE_LOCK__) + NKern::RestoreInterrupts(irq); +#endif + return ETrue; + + +trace_dropped: +#if !defined(__SMP__) || !defined(__USE_BTRACE_LOCK__) + NKern::RestoreInterrupts(irq); +#endif + return ETrue; + +trace_off: +#if !defined(__SMP__) || !defined(__USE_BTRACE_LOCK__) + NKern::RestoreInterrupts(irq); +#endif + return ETrue; +// return EFalse; + } + + +TBool SBTraceData::CheckFilter2(TUint32) + { + return TRUE; + } + +void InitBTraceHandler() + { + TInt cpu; +#ifdef __SMP__ + TInt ncpus = NKern::NumberOfCpus(); +#else + TInt ncpus = 1; +#endif + for (cpu=0; cpu