tracesrv/tracecore/btrace_handler/src/TraceCoreMediaWriter.cpp
changeset 56 aa2539c91954
--- /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 <kernel/kernel.h>
+#include <nkern/nk_priv.h> // 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