diff -r a151135b0cf9 -r aa2539c91954 tracesrv/tracecore/btrace_handler/src/TraceCoreMediaWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tracesrv/tracecore/btrace_handler/src/TraceCoreMediaWriter.cpp Fri Oct 08 14:56:39 2010 +0300 @@ -0,0 +1,587 @@ +// Copyright (c) 2007-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 Core +// + +#include +#include // Symbian internal -> Needed for kernel lock and NThread mutex state + +#include "TraceCoreMediaWriter.h" +#include "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "TraceCoreMediaWriterTraces.h" +#endif + + + +//- Internal definitions ---------------------------------------------------- +#ifdef MEDIA_WRITER_STATISTICS + +/** + * Collects statistics about the media writer + */ +class TMediaWriterStatistics + { +public: + TUint32 iTraces; // Number of traces received + TUint32 iSent; // Number of traces sent + TUint32 iFailed; // Number of traces failed to send + TUint32 iMissed; // Number of traces missed due to not enough buffers + TUint32 iSleepCount; // Number of times the threads have slept waiting for buffers + TUint32 iCannotSleepCount; // Number of times a thread could not be slept when it should + TUint32 iBlockCount; // Number of times a thread has been blocked waiting for buffers + TUint32 iCannotBlockCount; // Number of times a thread could not be blocked when it should + TUint32 iWaitDidNotHelp; // Number of waits that did not help + TUint32 iMinFreeBuffers; // Minimum number of free buffers since boot + TUint32 iInvalidContextTrace; // Number of IRQ / IDFC traces ignored + TUint32 iIdlePings; // Statistics are written after some idle time + TBool iStatisticsChanged; // Flags indicating change in statistics + }; + +/** + * Macro to wrap statistics-related code + */ +#define STATS( x ) x + +/** + * Initializes the statistics structure + */ +TBool DTraceCoreMediaWriter::InitStatistics() + { + iStatistics = new TMediaWriterStatistics(); + return iStatistics != NULL; + } + +/** + * Number of idle timer pings before sending statistics + */ +const TUint KPingsBeforeStatistics = 640; + +#else + +/** + * Empty class for statistics if flag is not set + */ +class TMediaWriterStatistics {}; + +#define STATS( x ) + +#endif + + +/** + * Max length of one trace + */ +const TUint32 KMaxBufLength = 256; + +/** + * Number of traces that can be buffered + */ +const TUint32 KBufCount = 2048; + +/** + * Send timer ping frequency in milliseconds + */ +const TUint KTimerPing = 10000; + +/** + * Starts to sleep threads when lots of buffers are in use + */ +const TUint32 KSleepBufLimit = 512; + +/** + * Ticks to sleep when using NKern::Sleep + */ +const TUint32 KSleepTicks = 1; + +/** + * Ticks to sleep when using Kern::NanoWait + */ +const TUint32 KNanoWaitTicks = 10000; + +/** + * Starts to block threads when running out of buffers + */ +const TUint32 KBlockBufLimit = 32; + +/** + * Maximum number of loops to run when blocking + */ +const TInt KMaxBlockLoops = 100; + + +/** + * Constructor + */ +EXPORT_C DTraceCoreMediaWriter::DTraceCoreMediaWriter() +: DTraceCoreWriter( EWriterTypeUSBPhonet ) +, iSendDfc( DTraceCoreMediaWriter::SendDfc, this, DTraceCore::GetActivationQ()), KDefaultDfcPriority ) +, iSendTimerActive( EFalse ) +, iFirstFreeBuffer( NULL ) +, iFirstReadyBuffer( NULL ) +, iLastReadyBuffer( NULL ) +, iTraceBuffers( NULL ) +, iFreeBuffers( KBufCount ) +, iSenderThread( NULL ) +, iLastTraceSent( 0 ) +, iStatistics( NULL ) + { + // No implementation in constructor + } + + +/** + * Destructor + */ +EXPORT_C DTraceCoreMediaWriter::~DTraceCoreMediaWriter() + { + + // Delete all trace buffers + for ( int i = 0; i < KBufCount; i++ ) + { + delete[] iTraceBuffers[ i ].iBuffer; + } + + delete[] iTraceBuffers; + delete iStatistics; + } + + +/** + * Registers this writer to TraceCore + * + * @return KErrNone if successful + */ +EXPORT_C TInt DTraceCoreMediaWriter::Register() + { + // Media writer uses the media interface from SendReceive + TInt retval( KErrGeneral ); + + // Get TraceCore + DTraceCore* traceCore = DTraceCore::GetInstance(); + if ( traceCore != NULL ) + { + + // Get MediaWriter interface + DTraceCoreMediaIf* writerIf = traceCore->GetSendReceive().GetMediaWriterIf(); + if ( writerIf != NULL ) + { + // Allocates memory for the trace buffers + TBool memOK = ETrue; + STATS( memOK = InitStatistics() ); + if (memOK) + { + iTraceBuffers = new TTraceBuffer[ KBufCount ]; + } + if ( iTraceBuffers == NULL ) + { + memOK = EFalse; + } + if ( memOK ) + { + for ( int i = 0; i < KBufCount && memOK; i++ ) + { + iTraceBuffers[ i ].iBuffer = new TUint8[ KMaxBufLength ]; + if (iTraceBuffers[ i ].iBuffer == NULL) + { + memOK = EFalse; + } + if ( memOK ) + { + // Initially all buffers are linked to iFirstFreeBuffer + if ( i < ( KBufCount - 1 ) ) + { + iTraceBuffers[ i ].iNext = &( iTraceBuffers[ i + 1 ] ); + } + } + else + { + memOK = EFalse; + } + } + } + else + { + memOK = EFalse; + } + + // Memory was allocated succesfully + if ( memOK ) + { + iFastCounterBetweenTraces = NKern::FastCounterFrequency() / GetTraceFrequency(); + iFirstFreeBuffer = iTraceBuffers; + + // Register to the TraceCore + retval = DTraceCoreWriter::Register(); + if ( retval == KErrNone ) + { + OstTrace0( TRACE_NORMAL, DTRACECOREMEDIAWRITER_REGISTER_WRITER_REGISTERED, + "DTraceCoreMediaWriter::Register - Media writer registered"); + iMediaIf = writerIf; + } + } + else + { + OstTrace0( TRACE_IMPORTANT , DTRACECOREMEDIAWRITER_REGISTER_MEM_ALLOC_FAILED, + "DTraceCoreMediaWriter::Register - Failed to allocate memory"); + retval = KErrNoMemory; + } + } + } + OstTrace1( TRACE_BORDER, DTRACECOREMEDIAWRITER_REGISTER_EXIT, "< DTraceCoreMediaWriter::Register. Ret:%d", retval ); + return retval; + } + + +/* + * Timer callback enques the DFC + * + * @param aMediaWriter the media writer + */ +void DTraceCoreMediaWriter::SendTimerCallback( TAny* aMediaWriter ) + { + DTraceCoreMediaWriter* writer = static_cast< DTraceCoreMediaWriter* >( aMediaWriter ); + writer->SendTimerCallback(); + } + + +/* + * Called from the static timer callback function + */ +void DTraceCoreMediaWriter::SendTimerCallback() + { + NKern::Lock(); + if ( iFreeBuffers < KBufCount ) + { + STATS( iStatistics->iIdlePings = 0 ); + + // Kernel locked, can call DoEnque + iSendDfc.DoEnque(); + } + else + { + STATS( iStatistics->iIdlePings++ ); +#ifndef MEDIA_WRITER_STATISTICS + // The callback timer is stopped unless writing statistics + iSendTimer.Cancel(); + iSendTimerActive = EFalse; +#endif + } + NKern::Unlock(); +#ifdef MEDIA_WRITER_STATISTICS + if ( iStatistics->iIdlePings == KPingsBeforeStatistics && iStatistics->iStatisticsChanged ) + { + + // Print out trace statistics + OstTraceExt4( TRACE_INTERNAL, DTRACECOREMEDIAWRITER_STATISTICS, + "DTraceCoreMediaWriter - Statistics. Traces:%u Missed:%u Sent:%u Failed:%u", + iStatistics->iTraces, iStatistics->iMissed, iStatistics->iSent, iStatistics->iFailed ); + + // Print out sleep / wait statistics + OstTraceExt4( TRACE_INTERNAL, DTRACECOREMEDIAWRITER_SLEEPS, + "DTraceCoreMediaWriter - Sleep / wait statistics. Sleeps:%u CannotSleeps:%u Blocks:%u CannotBlocks:%u", + iStatistics->iSleepCount, iStatistics->iCannotSleepCount, iStatistics->iBlockCount, + iStatistics->iCannotBlockCount ); + + // Print out timer values + OstTraceExt4( TRACE_INTERNAL, DTRACECOREMEDIAWRITER_TIMERS, + "DTraceCoreMediaWriter - Timer values %d %d %d %d", + (TInt)NKern::TickCount(), (TInt)NKern::FastCounter(), (TInt)NKern::TickPeriod(), + (TInt)NKern::FastCounterFrequency() ); + + // Print out misc statistics + OstTraceExt3( TRACE_INTERNAL, DTRACECOREMEDIAWRITER_MISC, + "DTraceCoreMediaWriter - Misc statistics WaitDidntHelp:%u MinFreeBuffers:%u InvalidContextTraces%u", + iStatistics->iWaitDidNotHelp, iStatistics->iMinFreeBuffers, iStatistics->iInvalidContextTrace ); + iStatistics->iStatisticsChanged = EFalse; + } +#endif + } + + +/** + * Starts an entry + * + * @param aType the type of the trace entry + * @return the entry ID that is passed to other Write-functions + */ +EXPORT_C TUint32 DTraceCoreMediaWriter::WriteStart( TWriterEntryType aType ) + { + // Detaches the first free buffer from the free buffers list + TInt context( NKern::CurrentContext() ); + TTraceBuffer* freeBuf = NULL; + STATS( iStatistics->iStatisticsChanged = ETrue ); + if ( context == NKern::EThread ) + { + NKern::Lock(); + freeBuf = iFirstFreeBuffer; + if ( freeBuf != NULL ) + { + iFirstFreeBuffer = freeBuf->iNext; + freeBuf->iNext = NULL; + freeBuf->iMissedBefore = 0; + iFreeBuffers--; + STATS( iStatistics->iMinFreeBuffers = ( iFreeBuffers < iStatistics->iMinFreeBuffers ) + ? iFreeBuffers : iStatistics->iMinFreeBuffers ); + // Timer is activated and stays active until all buffers are empty + if ( !iSendTimerActive ) + { + iSendTimer.Periodic( KTimerPing, SendTimerCallback, this ); + iSendTimerActive = ETrue; + } + } + else + { + iFirstReadyBuffer->iMissedBefore++; + STATS( iStatistics->iMissed++ ); + } + NKern::Unlock(); + if ( freeBuf != NULL ) + { + StartBuffer( aType, *freeBuf ); + } + } + else + { + STATS( iStatistics->iInvalidContextTrace++ ); + freeBuf = NULL; + } + return ( TUint32 )freeBuf; + } + + +/** + * Ends an entry + * + * @param aEntryId the entry ID returned by WriteStart + */ +EXPORT_C void DTraceCoreMediaWriter::WriteEnd( TUint32 aEntryId ) + { + if ( aEntryId != 0 ) + { + TTraceBuffer* buf = ( TTraceBuffer* )aEntryId; + EndBuffer( *buf ); + // If there's no existing ready buffers, the new buffer is set as first. + // Otherwise, it is assigned to the end of the ready buffers queue + NKern::Lock(); + if ( iFirstReadyBuffer == NULL ) + { + iFirstReadyBuffer = buf; + } + else + { + iLastReadyBuffer->iNext = buf; + } + iLastReadyBuffer = buf; + STATS( iStatistics->iTraces++ ); + NKern::Unlock(); + // Queues the DFC that sends the trace out. The DFC checks the timestamp of the + // last trace sent, so the trace might not be immediately sent out. + iSendDfc.Enque(); + + // If the number of buffers is below the sleep limit, this thread is slept for a while. + // If the number of buffers is below the block limit after sleep, + // this thread is slept for longer time + // Pre-conditions for Sleep: + // - Sleeping thread must not be the one sending traces out + // - Kernel must not be locked + // - Fast mutex must not be held by the thread + if ( iFreeBuffers < KSleepBufLimit ) + { + NThread* thread( NKern::CurrentThread() ); + if ( thread != iSenderThread && thread->iHeldFastMutex == NULL && !TScheduler::Ptr()->iKernCSLocked ) + { + STATS( iStatistics->iSleepCount++ ); + // User-side threads can use NKern::Sleep +#ifdef __WINS__ + TInt type = 0; +#else + TInt type = NKern::CurrentThread()->UserContextType(); +#endif + Block( type ); + if ( iFreeBuffers < KBlockBufLimit ) + { + STATS( iStatistics->iBlockCount++ ); + TInt loops = 0; + while ( ( iFreeBuffers < KBlockBufLimit ) && ( ++loops ) < KMaxBlockLoops ) + { + iSendDfc.Enque(); + Block( type ); + } + if ( loops == KMaxBlockLoops ) + { + STATS( iStatistics->iWaitDidNotHelp++ ); + } + } + } + else + { + STATS( iStatistics->iCannotSleepCount++ ); + if ( iFreeBuffers < KBlockBufLimit ) + { + STATS( iStatistics->iCannotBlockCount++ ); + } + } + } + } + } + + +/** + * Blocks the current thread for a while + * + * @param aType the context type from NThread::UserContextType + */ +void DTraceCoreMediaWriter::Block( TInt aType ) + { +#ifdef __WINS__ + ( void )aType; + NKern::Sleep( KSleepTicks ); +#else + if ( aType == NThread::EContextExec ) + { + NKern::Sleep( KSleepTicks ); + } + else + { + Kern::NanoWait( KNanoWaitTicks ); + } +#endif + } + + +/** + * Writes 8-bit data to given entry + * + * @param aEntryId the entry ID returned by WriteStart + * @param aData the trace data + */ +EXPORT_C void DTraceCoreMediaWriter::WriteData( TUint32 aEntryId, TUint8 aData ) + { + if ( aEntryId != 0 ) + { + TTraceBuffer* buf = ( TTraceBuffer* )aEntryId; + if ( buf->iLength < KMaxBufLength ) + { + *( buf->iBuffer + buf->iLength ) = aData; + buf->iLength++; + } + } + } + + +/** + * Writes 16-bit data to given entry + * + * @param aEntryId the entry ID returned by WriteStart + * @param aData the trace data + */ +EXPORT_C void DTraceCoreMediaWriter::WriteData( TUint32 aEntryId, TUint16 aData ) + { + if ( aEntryId != 0 ) + { + TTraceBuffer* buf = ( TTraceBuffer* )aEntryId; + if ( buf->iLength + sizeof ( TUint16 ) <= KMaxBufLength ) + { + *( ( TUint16* )( buf->iBuffer + buf->iLength ) ) = aData; + buf->iLength += sizeof ( TUint16 ); + } + } + } + + +/** + * Writes 32-bit data to given entry + * + * @param aEntryId the entry ID returned by WriteStart + * @param aData the trace data + */ +EXPORT_C void DTraceCoreMediaWriter::WriteData( TUint32 aEntryId, TUint32 aData ) + { + if ( aEntryId != 0 ) + { + TTraceBuffer* buf = ( TTraceBuffer* )aEntryId; + if ( buf->iLength + sizeof ( TUint32 ) <= KMaxBufLength ) + { + *( ( TUint32* )( buf->iBuffer + buf->iLength ) ) = SWAP_DATA( aData ); + buf->iLength += sizeof ( TUint32 ); + } + } + } + + +/** + * DFC for sending data + * + * @param aMediaWriter the media writer + */ +void DTraceCoreMediaWriter::SendDfc( TAny* aMediaWriter ) + { + DTraceCoreMediaWriter* writer = static_cast< DTraceCoreMediaWriter* >( aMediaWriter ); + writer->SendDfc(); + } + + +/** + * Called from static DFC function to send a buffer + */ +void DTraceCoreMediaWriter::SendDfc() + { + if ( iSenderThread == NULL ) + { + iSenderThread = NKern::CurrentThread(); + } + TUint32 time = NKern::FastCounter(); + // Timestamp is checked so that this does not send too frequently + // Otherwise the USB Phonet Link will crash + // FastCounter may overflow, so the less than check is also needed + if ( time > ( iLastTraceSent + iFastCounterBetweenTraces ) || ( time < iLastTraceSent ) ) + { + // Gets the first buffer that is ready for sending. + // Assigns the next buffer as first ready buffer + NKern::Lock(); + TTraceBuffer* buf = iFirstReadyBuffer; + if ( buf != NULL ) + { + iFirstReadyBuffer = buf->iNext; + } + NKern::Unlock(); + if ( buf != NULL ) + { + TPtrC8 ptr( buf->iBuffer, buf->iLength ); + TInt res = iMediaIf->SendTrace( ptr ); + // After sending the buffer is moved to the free buffers list + NKern::Lock(); + if ( res != KErrNone ) + { + STATS( iStatistics->iFailed++ ); + if ( iFirstReadyBuffer != NULL ) + { + iFirstReadyBuffer->iMissedBefore += buf->iMissedBefore; + } + } + else + { + STATS( iStatistics->iSent++ ); + } + buf->iNext = iFirstFreeBuffer; + iFirstFreeBuffer = buf; + iFreeBuffers++; + NKern::Unlock(); + iLastTraceSent = time; + } + } + } + +// End of File