FreeRTOS-Kernel/FreeRTOS-Plus/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c
Soren Ptak 3a2f6646f0
Use CI-CD-Github-Actions for spelling and formatting, add in the bot formatting action, update the CI-CD workflow files. Fix incorrect spelling and formatting on files. (#1083)
* Use new version of CI-CD Actions,  checkout@v3 instead of checkout@v2 on all jobs
* Use cSpell spell check, and use ubuntu-20.04 for formatting check
* Add in bot formatting action
* Update freertos_demo.yml and freertos_plus_demo.yml files to increase github log readability
* Add in a Qemu demo onto the workflows.
2023-09-06 12:35:37 -07:00

3505 lines
155 KiB
C

/*
* Trace Recorder for Tracealyzer v4.6.0
* Copyright 2021 Percepio AB
* www.percepio.com
*
* SPDX-License-Identifier: Apache-2.0
*
* The generic core of the trace recorder's snapshot mode.
*/
#include <trcRecorder.h>
#if ( TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT )
#if ( TRC_USE_TRACEALYZER_RECORDER == 1 )
#include <string.h>
#include <stdarg.h>
#ifndef TRC_CFG_RECORDER_DATA_INIT
#define TRC_CFG_RECORDER_DATA_INIT 1
#endif
#if ( ( TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR ) || ( TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR ) )
#error "CUSTOM timestamping mode is not (yet) supported in snapshot mode!"
#endif
/* DO NOT CHANGE */
#define TRACE_MINOR_VERSION 7
/* Keeps track of the task's stack low mark */
typedef struct
{
void * tcb;
uint32_t uiPreviousLowMark;
} TaskStackMonitorEntry_t;
TraceKernelPortDataBuffer_t xKernelPortDataBuffer;
#if ( TRC_CFG_INCLUDE_ISR_TRACING == 1 )
/*******************************************************************************
* isrstack
*
* Keeps track of nested interrupts.
******************************************************************************/
static traceHandle isrstack[ TRC_CFG_MAX_ISR_NESTING ];
/*******************************************************************************
* isPendingContextSwitch
*
* Used to indicate if there is a pending context switch.
* If there is a pending context switch the recorder will not create an event
* when returning from the ISR.
******************************************************************************/
int32_t isPendingContextSwitch = 0;
#endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1) */
/*******************************************************************************
* readyEventsEnabled
*
* This can be used to dynamically disable ready events.
******************************************************************************/
#if !defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1
static int readyEventsEnabled = 1;
#endif /*!defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1*/
/*******************************************************************************
* uiTraceTickCount
*
* This variable is should be updated by the Kernel tick interrupt. This does
* not need to be modified when developing a new timer port. It is preferred to
* keep any timer port changes in the HWTC macro definitions, which typically
* give sufficient flexibility.
******************************************************************************/
uint32_t uiTraceTickCount = 0;
/*******************************************************************************
* trace_disable_timestamp
*
* This can be used to disable timestamps as it will cause
* prvTracePortGetTimeStamp() to return the previous timestamp.
******************************************************************************/
uint32_t trace_disable_timestamp = 0;
/*******************************************************************************
* last_timestamp
*
* The most recent timestamp.
******************************************************************************/
static uint32_t last_timestamp = 0;
/*******************************************************************************
* uiTraceSystemState
*
* Indicates if we are currently performing a context switch or just running application code.
******************************************************************************/
volatile uint32_t uiTraceSystemState = TRC_STATE_IN_STARTUP;
/*******************************************************************************
* recorder_busy
*
* Flag that shows if inside a critical section of the recorder.
******************************************************************************/
volatile int recorder_busy = 0;
/*******************************************************************************
* timestampFrequency
*
* Holds the value set by vTraceSetFrequency.
******************************************************************************/
uint32_t timestampFrequency = 0;
/*******************************************************************************
* nISRactive
*
* The number of currently active (including preempted) ISRs.
******************************************************************************/
int8_t nISRactive = 0;
/*******************************************************************************
* handle_of_last_logged_task
*
* The current task.
******************************************************************************/
traceHandle handle_of_last_logged_task = 0;
/*******************************************************************************
* vTraceStopHookPtr
*
* Called when the recorder is stopped, set by vTraceSetStopHook.
******************************************************************************/
TRACE_STOP_HOOK vTraceStopHookPtr = ( TRACE_STOP_HOOK ) 0;
/*******************************************************************************
* init_hwtc_count
*
* Initial TRC_HWTC_COUNT value, for detecting if the time-stamping source is
* enabled. If using the OS periodic timer for time-stamping, this might not
* have been configured on the earliest events during the startup.
******************************************************************************/
uint32_t init_hwtc_count;
/*******************************************************************************
* CurrentFilterMask
*
* The filter mask that will be checked against each object's FilterGroup to see
* if they should be included in the trace or not.
******************************************************************************/
uint16_t CurrentFilterMask TRC_CFG_RECORDER_DATA_ATTRIBUTE;
/*******************************************************************************
* CurrentFilterGroup
*
* The current filter group that will be assigned to newly created objects.
******************************************************************************/
uint16_t CurrentFilterGroup TRC_CFG_RECORDER_DATA_ATTRIBUTE;
/*******************************************************************************
* objectHandleStacks
*
* A set of stacks that keeps track of available object handles for each class.
* The stacks are empty initially, meaning that allocation of new handles will be
* based on a counter (for each object class). Any delete operation will
* return the handle to the corresponding stack, for reuse on the next allocate.
******************************************************************************/
objectHandleStackType objectHandleStacks TRC_CFG_RECORDER_DATA_ATTRIBUTE;
/*******************************************************************************
* traceErrorMessage
*
* The last error message of the recorder. NULL if no error message.
******************************************************************************/
const char * traceErrorMessage TRC_CFG_RECORDER_DATA_ATTRIBUTE;
#if defined( TRC_CFG_ENABLE_STACK_MONITOR ) && ( TRC_CFG_ENABLE_STACK_MONITOR == 1 ) && ( TRC_CFG_SCHEDULING_ONLY == 0 )
/*******************************************************************************
* tasksInStackMonitor
*
* Keeps track of all stack low marks for tasks.
******************************************************************************/
TaskStackMonitorEntry_t tasksInStackMonitor[ TRC_CFG_STACK_MONITOR_MAX_TASKS ] TRC_CFG_RECORDER_DATA_ATTRIBUTE;
/*******************************************************************************
* tasksNotIncluded
*
* The number of tasks that did not fit in the stack monitor.
******************************************************************************/
int tasksNotIncluded TRC_CFG_RECORDER_DATA_ATTRIBUTE;
#endif /* defined(TRC_CFG_ENABLE_STACK_MONITOR) && (TRC_CFG_ENABLE_STACK_MONITOR == 1) && (TRC_CFG_SCHEDULING_ONLY == 0) */
/*******************************************************************************
* RecorderData
*
* The main data structure in snapshot mode, when using the default static memory
* allocation (TRC_RECORDER_BUFFER_ALLOCATION_STATIC). The recorder uses a pointer
* RecorderDataPtr to access the data, to also allow for dynamic or custom data
* allocation (see TRC_CFG_RECORDER_BUFFER_ALLOCATION).
******************************************************************************/
#if ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC )
RecorderDataType RecorderData TRC_CFG_RECORDER_DATA_ATTRIBUTE;
#endif
/* Pointer to the main data structure, when in snapshot mode */
RecorderDataType * RecorderDataPtr TRC_CFG_RECORDER_DATA_ATTRIBUTE;
#if ( TRC_CFG_RECORDER_DATA_INIT != 0 )
uint32_t RecorderInitialized = 0;
#else /* (TRC_CFG_RECORDER_DATA_INIT != 0) */
uint32_t RecorderInitialized TRC_CFG_RECORDER_DATA_ATTRIBUTE;
#endif /* (TRC_CFG_RECORDER_DATA_INIT != 0) */
/*************** Private Functions *******************************************/
static void prvStrncpy( char * dst,
const char * src,
uint32_t maxLength );
static uint8_t prvTraceGetObjectState( uint8_t objectclass,
traceHandle id );
static void prvTraceGetChecksum( const char * pname,
uint8_t * pcrc,
uint8_t * plength );
static void * prvTraceNextFreeEventBufferSlot( void );
static uint16_t prvTraceGetDTS( uint16_t param_maxDTS );
static TraceStringHandle_t prvTraceOpenSymbol( const char * name,
TraceStringHandle_t userEventChannel );
static void prvTraceUpdateCounters( void );
void vTraceStoreMemMangEvent( uint32_t ecode,
uint32_t address,
int32_t signed_size );
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
static void prvCheckDataToBeOverwrittenForMultiEntryEvents( uint8_t nEntries );
#endif
static TraceStringHandle_t prvTraceCreateSymbolTableEntry( const char * name,
uint8_t crc6,
uint8_t len,
TraceStringHandle_t channel );
static TraceStringHandle_t prvTraceLookupSymbolTableEntry( const char * name,
uint8_t crc6,
uint8_t len,
TraceStringHandle_t channel );
#if ( TRC_CFG_INCLUDE_ISR_TRACING == 0 )
/* ISR tracing is turned off */
void prvTraceIncreaseISRActive( void );
void prvTraceDecreaseISRActive( void );
#endif /*(TRC_CFG_INCLUDE_ISR_TRACING == 0)*/
#if ( TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1 )
static uint8_t prvTraceGet8BitHandle( traceHandle handle );
#else
#define prvTraceGet8BitHandle( x ) ( ( uint8_t ) x )
#endif
#if ( TRC_CFG_SCHEDULING_ONLY == 0 )
static uint32_t prvTraceGetParam( uint32_t,
uint32_t );
#endif
/*******************************************************************************
* prvTracePortGetTimeStamp
*
* Returns the current time based on the HWTC macros which provide a hardware
* isolation layer towards the hardware timer/counter.
*
* The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
* or the trace recorder library. Typically you should not need to change
* the code of prvTracePortGetTimeStamp if using the HWTC macros.
*
******************************************************************************/
void prvTracePortGetTimeStamp( uint32_t * puiTimestamp );
static void prvTraceTaskInstanceFinish( int8_t direct );
/*******************************************************************************
* prvTraceInitTimestamps
*
* This will only be called once the recorder is started, and we can assume that
* all hardware has been initialized.
******************************************************************************/
static void prvTraceInitTimestamps( void );
static void prvTraceStart( void );
static void prvTraceStop( void );
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
#if ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 )
static void vTraceUBData_Helper( traceUBChannel channelPair,
va_list vl );
static void prvTraceUBHelper1( traceUBChannel channel,
TraceStringHandle_t eventLabel,
TraceStringHandle_t formatLabel,
va_list vl );
static void prvTraceUBHelper2( traceUBChannel channel,
uint32_t * data,
uint32_t noOfSlots );
#endif /* (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1) */
#endif /* ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) */
uint16_t uiIndexOfObject( traceHandle objecthandle,
uint8_t objectclass );
/*******************************************************************************
* prvTraceError
*
* Called by various parts in the recorder. Stops the recorder and stores a
* pointer to an error message, which is printed by the monitor task.
******************************************************************************/
void prvTraceError( const char * msg );
/********* Public Functions **************************************************/
#if ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM )
traceResult xTraceSetBuffer( TraceRecorderDataBuffer_t * pxBuffer )
{
if( pxBuffer == 0 )
{
return TRC_FAIL;
}
RecorderDataPtr = ( RecorderDataType * ) pxBuffer;
return TRC_SUCCESS;
}
#endif /* if ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM ) */
traceResult xTraceGetEventBuffer( void ** ppvBuffer,
TraceUnsignedBaseType_t * puiSize )
{
if( ( ppvBuffer == 0 ) || ( puiSize == 0 ) )
{
return TRC_FAIL;
}
*ppvBuffer = ( void * ) RecorderDataPtr;
*puiSize = sizeof( RecorderDataType );
return TRC_SUCCESS;
}
traceResult xTraceEnable( uint32_t uiStartOption )
{
/* Make sure recorder data is initialized */
if( xTraceInitialize() == TRC_FAIL )
{
return TRC_FAIL;
}
if( uiStartOption == TRC_START )
{
if( xTraceKernelPortEnable() == TRC_FAIL )
{
return TRC_FAIL;
}
prvTraceInitTimestamps();
prvTraceStart();
}
else if( uiStartOption == TRC_START_AWAIT_HOST )
{
prvTraceError( "xTraceEnable(TRC_START_AWAIT_HOST) not allowed in Snapshot mode" );
return TRC_FAIL;
}
else if( uiStartOption != TRC_START_FROM_HOST )
{
prvTraceError( "xTraceEnable(TRC_START_FROM_HOST) not allowed in Snapshot mode" );
return TRC_FAIL;
}
return TRC_SUCCESS;
}
traceResult xTraceDisable( void )
{
prvTraceStop();
return TRC_SUCCESS;
}
void vTraceSetStopHook( TRACE_STOP_HOOK stopHookFunction )
{
vTraceStopHookPtr = stopHookFunction;
}
void vTraceClear( void )
{
TRACE_ALLOC_CRITICAL_SECTION();
trcCRITICAL_SECTION_BEGIN();
RecorderDataPtr->absTimeLastEventSecond = 0;
RecorderDataPtr->absTimeLastEvent = 0;
RecorderDataPtr->nextFreeIndex = 0;
RecorderDataPtr->numEvents = 0;
RecorderDataPtr->bufferIsFull = 0;
traceErrorMessage = 0;
RecorderDataPtr->internalErrorOccurred = 0;
( void ) memset( RecorderDataPtr->eventData, 0, RecorderDataPtr->maxEvents * 4 );
handle_of_last_logged_task = 0;
trcCRITICAL_SECTION_END();
}
static void prvTraceStart( void )
{
traceHandle handle;
TRACE_ALLOC_CRITICAL_SECTION();
handle = 0;
if( RecorderDataPtr == 0 )
{
TRACE_ASSERT( RecorderDataPtr != 0, "Recorder not initialized. Use vTraceEnable() instead!", TRC_UNUSED );
return;
}
if( RecorderDataPtr->recorderActive == 1 )
{
return; /* Already running */
}
if( traceErrorMessage == 0 )
{
trcCRITICAL_SECTION_BEGIN();
RecorderDataPtr->recorderActive = 1;
handle = TRACE_GET_TASK_NUMBER( TRACE_GET_CURRENT_TASK() );
if( handle == 0 )
{
/* This occurs if the scheduler is not yet started.
* This creates a dummy "(startup)" task entry internally in the
* recorder */
handle = prvTraceGetObjectHandle( TRACE_CLASS_TASK );
prvTraceSetObjectName( TRACE_CLASS_TASK, handle, "(startup)" );
prvTraceSetPriorityProperty( TRACE_CLASS_TASK, handle, 0 );
}
prvTraceStoreTaskswitch( handle ); /* Register the currently running task */
trcCRITICAL_SECTION_END();
}
}
/*******************************************************************************
* prvTraceStop
*
* Stops the recorder. The recording can be resumed by calling vTraceStart.
* This does not reset the recorder. Use vTraceClear if that is desired.
******************************************************************************/
static void prvTraceStop( void )
{
if( RecorderDataPtr != 0 )
{
RecorderDataPtr->recorderActive = 0;
}
if( vTraceStopHookPtr != ( TRACE_STOP_HOOK ) 0 )
{
( *vTraceStopHookPtr )(); /* An application call-back function. */
}
}
/*******************************************************************************
* xTraceIsRecorderEnabled
* Returns true (1) if the recorder is enabled (i.e. is recording), otherwise 0.
******************************************************************************/
uint32_t xTraceIsRecorderEnabled( void )
{
if( ( RecorderInitialized == 1 ) && ( RecorderDataPtr != 0 ) )
{
return RecorderDataPtr->recorderActive;
}
else
{
return 0;
}
}
/******************************************************************************
* xTraceIsRecorderInitialized
*
* Returns true (1) if the recorder is initialized.
******************************************************************************/
uint32_t xTraceIsRecorderInitialized( void )
{
return RecorderInitialized;
}
/*******************************************************************************
* xTraceErrorGetLast
*
* Gives the last error message, if any. NULL if no error message is stored.
* Any error message is also presented when opening a trace file.
******************************************************************************/
const char * xTraceErrorGetLast( void )
{
return traceErrorMessage;
}
/*******************************************************************************
* vTraceClearError
*
* Removes any previous error message generated by recorder calling prvTraceError.
* By calling this function, it may be possible to start/restart the trace
* despite errors in the recorder, but there is no guarantee that the trace
* recorder will work correctly in that case, depending on the type of error.
******************************************************************************/
void vTraceClearError( void )
{
traceErrorMessage = 0;
if( RecorderDataPtr != 0 )
{
RecorderDataPtr->internalErrorOccurred = 0;
}
}
/*******************************************************************************
* xTraceGetTraceBuffer
*
* Returns a pointer to the recorder data structure. Use this together with
* uiTraceGetTraceBufferSize if you wish to implement an own store/upload
* solution, e.g., in case a debugger connection is not available for uploading
* the data.
******************************************************************************/
void * xTraceGetTraceBuffer( void )
{
return RecorderDataPtr;
}
/*******************************************************************************
* uiTraceGetTraceBufferSize
*
* Gets the size of the recorder data structure. For use together with
* vTraceGetTraceBuffer if you wish to implement an own store/upload solution,
* e.g., in case a debugger connection is not available for uploading the data.
******************************************************************************/
uint32_t uiTraceGetTraceBufferSize( void )
{
return sizeof( RecorderDataType );
}
/*******************************************************************************
* prvTraceInitTimestamps
*
* If vTraceEnable(TRC_INIT) was called BEFORE the clock was initialized, this
* function must be called AFTER the clock is initialized to set a proper
* initial timestamp value. If vTraceEnable(...) is only called AFTER clock is
* initialized, there is no need to call this function.
******************************************************************************/
static void prvTraceInitTimestamps( void )
{
init_hwtc_count = TRC_HWTC_COUNT;
}
/******************************************************************************
* prvTraceTaskInstanceFinish
*
* Private common function for the vTraceTaskInstanceFinishXXX functions.
*****************************************************************************/
static void prvTraceTaskInstanceFinish( int8_t direct )
{
TaskInstanceStatusEvent * tis;
uint8_t dts45;
TRACE_ALLOC_CRITICAL_SECTION();
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
dts45 = ( uint8_t ) prvTraceGetDTS( 0xFF );
tis = ( TaskInstanceStatusEvent * ) prvTraceNextFreeEventBufferSlot();
if( tis != 0 )
{
if( direct == 0 )
{
tis->type = TASK_INSTANCE_FINISHED_NEXT_KSE;
}
else
{
tis->type = TASK_INSTANCE_FINISHED_DIRECT;
}
tis->dts = dts45;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
/******************************************************************************
* xTraceTaskInstanceFinishedNext(void)
*
* Marks the current task instance as finished on the next kernel call.
*
* If that kernel call is blocking, the instance ends after the blocking event
* and the corresponding return event is then the start of the next instance.
* If the kernel call is not blocking, the viewer instead splits the current
* fragment right before the kernel call, which makes this call the first event
* of the next instance.
*
* See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
*
* Example:
*
* while(1)
* {
* xQueueReceive(CommandQueue, &command, timeoutDuration);
* processCommand(command);
* xTraceTaskInstanceFinishedNext();
* }
*****************************************************************************/
traceResult xTraceTaskInstanceFinishedNext( void )
{
prvTraceTaskInstanceFinish( 0 );
return TRC_SUCCESS;
}
/******************************************************************************
* xTraceTaskInstanceFinishedNow(void)
*
* Marks the current task instance as finished at this very instant.
* This makes the viewer to splits the current fragment at this point and begin
* a new actor instance.
*
* See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
*
* Example:
*
* This example will generate two instances for each loop iteration.
* The first instance ends at xTraceTaskInstanceFinishedNow(), while the second
* instance ends at the next xQueueReceive call.
*
* while (1)
* {
* xQueueReceive(CommandQueue, &command, timeoutDuration);
* ProcessCommand(command);
* xTraceTaskInstanceFinishedNow();
* DoSometingElse();
* xTraceTaskInstanceFinishedNext();
* }
*****************************************************************************/
traceResult xTraceTaskInstanceFinishedNow( void )
{
prvTraceTaskInstanceFinish( 1 );
return TRC_SUCCESS;
}
/*******************************************************************************
* Interrupt recording functions
******************************************************************************/
#if ( TRC_CFG_INCLUDE_ISR_TRACING == 1 )
/*******************************************************************************
* xTraceSetISRProperties
*
* Stores a name and priority level for an Interrupt Service Routine, to allow
* for better visualization. Returns a traceHandle used by vTraceStoreISRBegin.
*
* Example:
* #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
* ...
* traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
* ...
* void ISR_handler()
* {
* vTraceStoreISRBegin(Timer1Handle);
* ...
* vTraceStoreISREnd(0);
* }
******************************************************************************/
traceHandle xTraceSetISRProperties( const char * name,
uint8_t priority )
{
static traceHandle handle = 0;
TRACE_ASSERT( RecorderDataPtr != 0, "Recorder not initialized, call vTraceEnable() first!", ( traceHandle ) 0 );
TRACE_ASSERT( handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ TRACE_CLASS_ISR ], "xTraceSetISRProperties: Invalid value for handle", 0 );
TRACE_ASSERT( name != 0, "xTraceSetISRProperties: name == NULL", 0 );
handle++;
prvTraceSetObjectName( TRACE_CLASS_ISR, handle, name );
prvTraceSetPriorityProperty( TRACE_CLASS_ISR, handle, priority );
return handle;
}
/*******************************************************************************
* vTraceStoreISRBegin
*
* Registers the beginning of an Interrupt Service Routine, using a traceHandle
* provided by xTraceSetISRProperties.
*
* Example:
* #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
* ...
* traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
* ...
* void ISR_handler()
* {
* vTraceStoreISRBegin(Timer1Handle);
* ...
* vTraceStoreISREnd(0);
* }
******************************************************************************/
void vTraceStoreISRBegin( traceHandle handle )
{
TRACE_ALLOC_CRITICAL_SECTION();
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "vTraceStoreISRBegin - recorder busy! See code comment." );
return;
}
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
uint16_t dts4;
TRACE_ASSERT( handle != 0, "vTraceStoreISRBegin: Invalid ISR handle (NULL)", TRC_UNUSED );
TRACE_ASSERT( handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ TRACE_CLASS_ISR ], "vTraceStoreISRBegin: Invalid ISR handle (> NISR)", TRC_UNUSED );
dts4 = ( uint16_t ) prvTraceGetDTS( 0xFFFF );
if( RecorderDataPtr->recorderActive ) /* Need to repeat this check! */
{
if( nISRactive < TRC_CFG_MAX_ISR_NESTING )
{
TSEvent * ts;
uint8_t hnd8 = prvTraceGet8BitHandle( handle );
isrstack[ nISRactive ] = handle;
nISRactive++;
ts = ( TSEvent * ) prvTraceNextFreeEventBufferSlot();
if( ts != 0 )
{
ts->type = TS_ISR_BEGIN;
ts->dts = dts4;
ts->objHandle = hnd8;
prvTraceUpdateCounters();
}
}
else
{
/* This should not occur unless something is very wrong */
prvTraceError( "Too many nested interrupts!" );
}
}
}
trcCRITICAL_SECTION_END();
}
/*******************************************************************************
* vTraceStoreISREnd
*
* Registers the end of an Interrupt Service Routine.
*
* The parameter pendingISR indicates if the interrupt has requested a
* task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the
* interrupt is assumed to return to the previous context.
*
* Example:
* #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt
* traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder
* ...
* traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1);
* ...
* void ISR_handler()
* {
* vTraceStoreISRBegin(traceHandleIsrTimer1);
* ...
* vTraceStoreISREnd(0);
* }
******************************************************************************/
void vTraceStoreISREnd( int pendingISR )
{
TSEvent * ts;
uint16_t dts5;
uint8_t hnd8 = 0, type = 0;
TRACE_ALLOC_CRITICAL_SECTION();
if( !RecorderDataPtr->recorderActive || !handle_of_last_logged_task )
{
return;
}
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "vTraceStoreISREnd - recorder busy! See code comment." );
return;
}
if( nISRactive == 0 )
{
prvTraceError( "Unmatched call to vTraceStoreISREnd (nISRactive == 0, expected > 0)" );
return;
}
trcCRITICAL_SECTION_BEGIN();
isPendingContextSwitch |= pendingISR; /* Is there a pending context switch right now? If so, we will not create an event since we will get an event when that context switch is executed. */
nISRactive--;
if( nISRactive > 0 )
{
/* Return to another ISR */
type = TS_ISR_RESUME;
hnd8 = prvTraceGet8BitHandle( isrstack[ nISRactive - 1 ] ); /* isrstack[nISRactive] is the handle of the ISR we're currently exiting. isrstack[nISRactive - 1] is the handle of the ISR that was executing previously. */
}
else if( ( isPendingContextSwitch == 0 ) || ( xTraceKernelPortIsSchedulerSuspended() ) )
{
/* Return to interrupted task, if no context switch will occur in between. */
type = TS_TASK_RESUME;
hnd8 = prvTraceGet8BitHandle( handle_of_last_logged_task );
}
if( type != 0 )
{
dts5 = ( uint16_t ) prvTraceGetDTS( 0xFFFF );
ts = ( TSEvent * ) prvTraceNextFreeEventBufferSlot();
if( ts != 0 )
{
ts->type = type;
ts->objHandle = hnd8;
ts->dts = dts5;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
#else /* if ( TRC_CFG_INCLUDE_ISR_TRACING == 1 ) */
/* ISR tracing is turned off */
void prvTraceIncreaseISRActive( void )
{
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
nISRactive++;
}
}
void prvTraceDecreaseISRActive( void )
{
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
nISRactive--;
}
}
#endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1)*/
/********************************************************************************/
/* User Event functions */
/********************************************************************************/
#define MAX_ARG_SIZE ( 4 + 32 )
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
static uint8_t writeInt8( void * buffer,
uint8_t i,
uint8_t value )
{
TRACE_ASSERT( buffer != 0, "writeInt8: buffer == NULL", 0 );
if( i >= MAX_ARG_SIZE )
{
return 255;
}
( ( uint8_t * ) buffer )[ i ] = value;
if( i + 1 > MAX_ARG_SIZE )
{
return 255;
}
return( ( uint8_t ) ( i + 1 ) );
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
static uint8_t writeInt16( void * buffer,
uint8_t i,
uint16_t value )
{
TRACE_ASSERT( buffer != 0, "writeInt16: buffer == NULL", 0 );
/* Align to multiple of 2 */
while( ( i % 2 ) != 0 )
{
if( i >= MAX_ARG_SIZE )
{
return 255;
}
( ( uint8_t * ) buffer )[ i ] = 0;
i++;
}
if( i + 2 > MAX_ARG_SIZE )
{
return 255;
}
( ( uint16_t * ) buffer )[ i / 2 ] = value;
return( ( uint8_t ) ( i + 2 ) );
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
static uint8_t writeInt32( void * buffer,
uint8_t i,
uint32_t value )
{
TRACE_ASSERT( buffer != 0, "writeInt32: buffer == NULL", 0 );
/* A 32 bit value should begin at an even 4-byte address */
while( ( i % 4 ) != 0 )
{
if( i >= MAX_ARG_SIZE )
{
return 255;
}
( ( uint8_t * ) buffer )[ i ] = 0;
i++;
}
if( i + 4 > MAX_ARG_SIZE )
{
return 255;
}
( ( uint32_t * ) buffer )[ i / 4 ] = value;
return( ( uint8_t ) ( i + 4 ) );
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_INCLUDE_FLOAT_SUPPORT ) )
static uint8_t writeFloat( void * buffer,
uint8_t i,
float value )
{
TRACE_ASSERT( buffer != 0, "writeFloat: buffer == NULL", 0 );
/* A 32 bit value should begin at an even 4-byte address */
while( ( i % 4 ) != 0 )
{
if( i >= MAX_ARG_SIZE )
{
return 255;
}
( ( uint8_t * ) buffer )[ i ] = 0;
i++;
}
if( i + 4 > MAX_ARG_SIZE )
{
return 255;
}
( ( float * ) buffer )[ i / 4 ] = value;
return i + 4;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_INCLUDE_FLOAT_SUPPORT ) ) */
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_INCLUDE_FLOAT_SUPPORT ) )
static uint8_t writeDouble( void * buffer,
uint8_t i,
double value )
{
uint32_t * dest;
uint32_t * src = ( uint32_t * ) &value;
TRACE_ASSERT( buffer != 0, "writeDouble: buffer == NULL", 0 );
/* The double is written as two 32 bit values, and should begin at an even
* 4-byte address (to avoid having to align with 8 byte) */
while( i % 4 != 0 )
{
if( i >= MAX_ARG_SIZE )
{
return 255;
}
( ( uint8_t * ) buffer )[ i ] = 0;
i++;
}
if( i + 8 > MAX_ARG_SIZE )
{
return 255;
}
dest = &( ( ( uint32_t * ) buffer )[ i / 4 ] );
dest[ 0 ] = src[ 0 ];
dest[ 1 ] = src[ 1 ];
return i + 8;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_INCLUDE_FLOAT_SUPPORT ) ) */
/*******************************************************************************
* prvTraceUserEventFormat
*
* Parses the format string and stores the arguments in the buffer.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
static uint8_t prvTraceUserEventFormat( const char * formatStr,
va_list vl,
uint8_t * buffer,
uint8_t byteOffset )
{
uint16_t formatStrIndex = 0;
uint8_t argCounter = 0;
uint8_t i = byteOffset;
while( formatStr[ formatStrIndex ] != '\0' )
{
if( formatStr[ formatStrIndex ] == '%' )
{
if( formatStr[ formatStrIndex + 1 ] == '%' )
{
formatStrIndex += 2;
continue;
}
/* We found a possible argument */
argCounter++;
formatStrIndex++;
while( ( formatStr[ formatStrIndex ] >= '0' && formatStr[ formatStrIndex ] <= '9' ) || formatStr[ formatStrIndex ] == '#' || formatStr[ formatStrIndex ] == '.' )
{
formatStrIndex++;
}
/* This check is necessary to avoid moving past end of string. */
if( formatStr[ formatStrIndex ] != '\0' )
{
switch( formatStr[ formatStrIndex ] )
{
case 'd':
i = writeInt32( buffer,
i,
( uint32_t ) va_arg( vl, uint32_t ) );
break;
case 'x':
case 'X':
case 'u':
i = writeInt32( buffer,
i,
( uint32_t ) va_arg( vl, uint32_t ) );
break;
case 's':
{
TraceStringHandle_t xString;
xTraceStringRegister( ( char * ) va_arg( vl, char * ), &xString );
i = writeInt16( buffer,
i,
( uint16_t ) xString );
}
break;
#if ( TRC_CFG_INCLUDE_FLOAT_SUPPORT )
/* Yes, "double" as type also in the float
* case. This since "float" is promoted into "double"
* by the va_arg stuff. */
case 'f':
i = writeFloat( buffer,
i,
( float ) va_arg( vl, double ) );
break;
#else
/* No support for floats, but attempt to store a float user event
* avoid a possible crash due to float reference. Instead store the
* data on uint_32 format (will not be displayed anyway). This is just
* to keep va_arg and i consistent. */
case 'f':
i = writeInt32( buffer,
i,
( uint32_t ) va_arg( vl, double ) );
break;
#endif /* if ( TRC_CFG_INCLUDE_FLOAT_SUPPORT ) */
case 'l':
formatStrIndex++;
switch( formatStr[ formatStrIndex ] )
{
#if ( TRC_CFG_INCLUDE_FLOAT_SUPPORT )
case 'f':
i = writeDouble( buffer,
i,
( double ) va_arg( vl, double ) );
break;
#else
/* No support for floats, but attempt to store a float user event
* avoid a possible crash due to float reference. Instead store the
* data on uint_32 format (will not be displayed anyway). This is just
* to keep va_arg and i consistent. */
case 'f':
i = writeInt32( buffer, /* In this case, the value will not be shown anyway */
i,
( uint32_t ) va_arg( vl, double ) );
i = writeInt32( buffer, /* Do it twice, to write in total 8 bytes */
i,
( uint32_t ) va_arg( vl, double ) );
break;
#endif /* if ( TRC_CFG_INCLUDE_FLOAT_SUPPORT ) */
default:
break;
}
break;
case 'h':
formatStrIndex++;
switch( formatStr[ formatStrIndex ] )
{
case 'd':
i = writeInt16( buffer,
i,
( uint16_t ) va_arg( vl, uint32_t ) );
break;
case 'u':
i = writeInt16( buffer,
i,
( uint16_t ) va_arg( vl, uint32_t ) );
break;
default:
break;
}
break;
case 'b':
formatStrIndex++;
switch( formatStr[ formatStrIndex ] )
{
case 'd':
i = writeInt8( buffer,
i,
( uint8_t ) va_arg( vl, uint32_t ) );
break;
case 'u':
i = writeInt8( buffer,
i,
( uint8_t ) va_arg( vl, uint32_t ) );
break;
default:
break;
}
break;
default:
/* False alarm: this wasn't a valid format specifier */
argCounter--;
break;
}
if( argCounter > 15 )
{
prvTraceError( "xTracePrintF - Too many arguments, max 15 allowed!" );
return 0;
}
}
else
{
break;
}
}
formatStrIndex++;
if( i == 255 )
{
prvTraceError( "xTracePrintF - Too large arguments, max 32 byte allowed!" );
return 0;
}
}
return ( uint8_t ) ( i + 3 ) / 4;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
/*******************************************************************************
* prvTraceClearChannelBuffer
*
* Clears a number of items in the channel buffer, starting from nextSlotToWrite.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
static void prvTraceClearChannelBuffer( uint32_t count )
{
uint32_t slots;
TRACE_ASSERT( ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ) >= count,
"prvTraceClearChannelBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED );
/* Check if we're close to the end of the buffer */
if( RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ) )
{
slots = ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ) -RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
( void ) memset( &RecorderDataPtr->userEventBuffer.channelBuffer[ RecorderDataPtr->userEventBuffer.nextSlotToWrite ], 0, slots );
( void ) memset( &RecorderDataPtr->userEventBuffer.channelBuffer[ 0 ], 0, ( count - slots ) );
}
else
{
( void ) memset( &RecorderDataPtr->userEventBuffer.channelBuffer[ RecorderDataPtr->userEventBuffer.nextSlotToWrite ], 0, count );
}
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/*******************************************************************************
* prvTraceCopyToDataBuffer
*
* Copies a number of items to the data buffer, starting from nextSlotToWrite.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
static void prvTraceCopyToDataBuffer( uint32_t * data,
uint32_t count )
{
uint32_t slots;
TRACE_ASSERT( data != 0,
"prvTraceCopyToDataBuffer: data == NULL.", TRC_UNUSED );
TRACE_ASSERT( count <= ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ),
"prvTraceCopyToDataBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED );
/* Check if we're close to the end of the buffer */
if( RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ) )
{
slots = ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ) -RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
( void ) memcpy( &RecorderDataPtr->userEventBuffer.dataBuffer[ RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4 ], data, slots * 4 );
( void ) memcpy( &RecorderDataPtr->userEventBuffer.dataBuffer[ 0 ], data + slots, ( count - slots ) * 4 );
}
else
{
( void ) memcpy( &RecorderDataPtr->userEventBuffer.dataBuffer[ RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4 ], data, count * 4 );
}
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/*******************************************************************************
* prvTraceUBHelper1
*
* Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on
* to the next helper function.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
static void prvTraceUBHelper1( traceUBChannel channel,
TraceStringHandle_t eventLabel,
TraceStringHandle_t formatLabel,
va_list vl )
{
uint32_t data[ ( 3 + MAX_ARG_SIZE ) / 4 ];
uint8_t byteOffset = 4; /* Need room for timestamp */
uint8_t noOfSlots;
if( channel == 0 )
{
/* We are dealing with an unknown channel format pair */
byteOffset = ( uint8_t ) ( byteOffset + 4 ); /* Also need room for channel and format */
( ( uint16_t * ) data )[ 2 ] = eventLabel;
( ( uint16_t * ) data )[ 3 ] = formatLabel;
}
noOfSlots = prvTraceUserEventFormat( ( char * ) &( RecorderDataPtr->SymbolTable.symbytes[ formatLabel + 4 ] ), vl, ( uint8_t * ) data, byteOffset );
prvTraceUBHelper2( channel, data, noOfSlots );
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/*******************************************************************************
* prvTraceUBHelper2
*
* This function simply copies the data buffer to the actual user event buffer.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
static void prvTraceUBHelper2( traceUBChannel channel,
uint32_t * data,
uint32_t noOfSlots )
{
static uint32_t old_timestamp = 0;
uint32_t old_nextSlotToWrite = 0;
TRACE_ALLOC_CRITICAL_SECTION();
TRACE_ASSERT( ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ) >= noOfSlots, "prvTraceUBHelper2: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED );
trcCRITICAL_SECTION_BEGIN();
/* Store the timestamp */
prvTracePortGetTimeStamp( data );
if( *data < old_timestamp )
{
RecorderDataPtr->userEventBuffer.wraparoundCounter++;
}
old_timestamp = *data;
/* Start by erasing any information in the channel buffer */
prvTraceClearChannelBuffer( noOfSlots );
prvTraceCopyToDataBuffer( data, noOfSlots ); /* Will wrap around the data if necessary */
old_nextSlotToWrite = RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Save the index that we want to write the channel data at when we're done */
RecorderDataPtr->userEventBuffer.nextSlotToWrite = ( RecorderDataPtr->userEventBuffer.nextSlotToWrite + noOfSlots ) % ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE ); /* Make sure we never end up outside the buffer */
/* Write to the channel buffer to indicate that this user event is ready to be used */
if( channel != 0 )
{
RecorderDataPtr->userEventBuffer.channelBuffer[ old_nextSlotToWrite ] = channel;
}
else
{
/* 0xFF indicates that this is not a normal channel id */
RecorderDataPtr->userEventBuffer.channelBuffer[ old_nextSlotToWrite ] = ( traceUBChannel ) 0xFF;
}
trcCRITICAL_SECTION_END();
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/*******************************************************************************
* xTraceRegisterUBChannel
*
* Registers a channel for Separated User Events, i.e., those stored in the
* separate user event buffer.
*
* Note: Only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is enabled in
* trcSnapshotConfig.h
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
traceUBChannel xTraceRegisterUBChannel( TraceStringHandle_t channel,
TraceStringHandle_t formatStr )
{
uint8_t i;
traceUBChannel retVal = 0;
TRACE_ALLOC_CRITICAL_SECTION();
TRACE_ASSERT( formatStr != 0, "xTraceRegisterChannelFormat: formatStr == 0", ( traceUBChannel ) 0 );
trcCRITICAL_SECTION_BEGIN();
for( i = 1; i <= ( TRC_CFG_UB_CHANNELS ); i++ ) /* Size of the channels buffer is TRC_CFG_UB_CHANNELS + 1. Index 0 is unused. */
{
if( ( RecorderDataPtr->userEventBuffer.channels[ i ].name == 0 ) && ( RecorderDataPtr->userEventBuffer.channels[ i ].defaultFormat == 0 ) )
{
/* Found empty slot */
RecorderDataPtr->userEventBuffer.channels[ i ].name = channel;
RecorderDataPtr->userEventBuffer.channels[ i ].defaultFormat = formatStr;
retVal = ( traceUBChannel ) i;
break;
}
if( ( RecorderDataPtr->userEventBuffer.channels[ i ].name == channel ) && ( RecorderDataPtr->userEventBuffer.channels[ i ].defaultFormat == formatStr ) )
{
/* Found a match */
retVal = ( traceUBChannel ) i;
break;
}
}
trcCRITICAL_SECTION_END();
return retVal;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/******************************************************************************
* vTraceUBData
*
* Slightly faster version of xTracePrintF() due to no lookups.
*
* Note: This is only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is
* enabled in trcSnapshotConfig.h
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
void vTraceUBData( traceUBChannel channelPair,
... )
{
va_list vl;
TRACE_ASSERT( channelPair != 0, "vTraceUBData: Not a valid traceUBChannel!", TRC_UNUSED );
va_start( vl, channelPair );
vTraceUBData_Helper( channelPair, vl );
va_end( vl );
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/* Extracts the channel name and format string from the traceUBChannel, then calls prvTraceUBHelper1. */
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
void vTraceUBData_Helper( traceUBChannel channelPair,
va_list vl )
{
TraceStringHandle_t channel;
TraceStringHandle_t formatStr;
TRACE_ASSERT( channelPair != 0, "vTraceUBData_Helper: channelPair == 0", TRC_UNUSED );
TRACE_ASSERT( channelPair <= ( TRC_CFG_UB_CHANNELS ), "vTraceUBData_Helper: ", TRC_UNUSED );
channel = RecorderDataPtr->userEventBuffer.channels[ channelPair ].name;
formatStr = RecorderDataPtr->userEventBuffer.channels[ channelPair ].defaultFormat;
prvTraceUBHelper1( channelPair, channel, formatStr, vl );
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) ) */
/******************************************************************************
* vTraceUBEvent
*
* Slightly faster version of ... due to no lookups.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) && ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 ) )
void vTraceUBEvent( traceUBChannel channelPair )
{
uint32_t data[ ( 3 + MAX_ARG_SIZE ) / 4 ];
TRACE_ASSERT( channelPair != 0, "vTraceUBEvent: channelPair == 0", TRC_UNUSED );
TRACE_ASSERT( channelPair <= ( TRC_CFG_UB_CHANNELS ), "vTraceUBEvent: ", TRC_UNUSED );
prvTraceUBHelper2( channelPair, data, 1 ); /* Only need one slot for timestamp */
}
#endif
/******************************************************************************
* xTracePrintF
*
* Generates User Event with formatted text and data, similar to a "printf".
* It is very fast compared to a normal "printf" since this function only
* stores the arguments. The actual formatting is done
* on the host PC when the trace is displayed in the viewer tool.
*
* User Event labels are created using xTraceStringRegister.
* Example:
*
* TraceStringHandle_t adc_uechannel;
* xTraceStringRegister("ADC User Events", &adc_uechannel);
* ...
* xTracePrintF(adc_uechannel,
* "ADC channel %d: %lf volts",
* ch, (double)adc_reading/(double)scale);
*
* Calling xTraceStringRegister multiple times will not create duplicate entries, but
* it is of course faster to just do it once, and then keep the handle for later
* use. If you don't have any data arguments, only a text label/string, it is
* better to use xTracePrint - it is faster.
*
* Format specifiers supported:
* %d - 32 bit signed integer
* %u - 32 bit unsigned integer
* %f - 32 bit float
* %s - string (is copied to the recorder symbol table)
* %hd - 16 bit signed integer
* %hu - 16 bit unsigned integer
* %bd - 8 bit signed integer
* %bu - 8 bit unsigned integer
* %lf - double-precision float (Note! See below...)
*
* Up to 15 data arguments are allowed, with a total size of maximum 32 byte.
* In case this is exceeded, the user event is changed into an error message.
*
* The data is stored in trace buffer, and is packed to allow storing multiple
* smaller data entries in the same 4-byte record, e.g., four 8-bit values.
* A string requires two bytes, as the symbol table is limited to 64K. Storing
* a double (%lf) uses two records, so this is quite costly. Use float (%f)
* unless the higher precision is really necessary.
*
* Note that the double-precision float (%lf) assumes a 64 bit double
* representation. This does not seem to be the case on e.g. PIC24 and PIC32.
* Before using a %lf argument on a 16-bit MCU, please verify that
* "sizeof(double)" actually gives 8 as expected. If not, use %f instead.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
traceResult xTracePrintF( TraceStringHandle_t eventLabel,
const char * formatStr,
... )
{
va_list vl;
va_start( vl, formatStr );
xTraceVPrintF( eventLabel, formatStr, vl );
va_end( vl );
return TRC_SUCCESS;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
/******************************************************************************
* xTraceVPrintF
*
* xTracePrintF variant that accepts a va_list.
* See xTracePrintF documentation for further details.
*
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
traceResult xTraceVPrintF( TraceStringHandle_t eventLabel,
const char * formatStr,
va_list vl )
{
#if ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0 )
uint32_t noOfSlots;
UserEvent * ue1;
uint32_t tempDataBuffer[ ( 3 + MAX_ARG_SIZE ) / 4 ];
TRACE_ALLOC_CRITICAL_SECTION();
TRACE_ASSERT( formatStr != 0, "vTraceVPrintF: formatStr == NULL", TRC_FAIL );
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
/* First, write the "primary" user event entry in the local buffer, but
* let the event type be "EVENT_BEING_WRITTEN" for now...*/
ue1 = ( UserEvent * ) ( &tempDataBuffer[ 0 ] );
ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */
noOfSlots = prvTraceUserEventFormat( formatStr, vl, ( uint8_t * ) tempDataBuffer, 4 );
/* Store the format string, with a reference to the channel symbol */
ue1->payload = prvTraceOpenSymbol( formatStr, eventLabel );
ue1->dts = ( uint8_t ) prvTraceGetDTS( 0xFF );
/* prvTraceGetDTS might stop the recorder in some cases... */
if( RecorderDataPtr->recorderActive )
{
/* If the data does not fit in the remaining main buffer, wrap around to
* 0 if allowed, otherwise stop the recorder and quit). */
if( RecorderDataPtr->nextFreeIndex + noOfSlots > RecorderDataPtr->maxEvents )
{
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
( void ) memset( &RecorderDataPtr->eventData[ RecorderDataPtr->nextFreeIndex * 4 ],
0,
( RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex ) * 4 );
RecorderDataPtr->nextFreeIndex = 0;
RecorderDataPtr->bufferIsFull = 1;
#else
/* Stop recorder, since the event data will not fit in the
* buffer and not circular buffer in this case... */
vTraceStop();
#endif
}
/* Check if recorder has been stopped (i.e., vTraceStop above) */
if( RecorderDataPtr->recorderActive )
{
/* Check that the buffer to be overwritten does not contain any user
* events that would be partially overwritten. If so, they must be "killed"
* by replacing the user event and following data with NULL events (i.e.,
* using a memset to zero).*/
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
prvCheckDataToBeOverwrittenForMultiEntryEvents( ( uint8_t ) noOfSlots );
#endif
/* Copy the local buffer to the main buffer */
( void ) memcpy( &RecorderDataPtr->eventData[ RecorderDataPtr->nextFreeIndex * 4 ],
tempDataBuffer,
noOfSlots * 4 );
/* Update the event type, i.e., number of data entries following the
* main USER_EVENT entry (Note: important that this is after the memcpy,
* but within the critical section!)*/
RecorderDataPtr->eventData[ RecorderDataPtr->nextFreeIndex * 4 ] =
( uint8_t ) ( USER_EVENT + noOfSlots - 1 );
/* Update the main buffer event index (already checked that it fits in
* the buffer, so no need to check for wrapping)*/
RecorderDataPtr->nextFreeIndex += noOfSlots;
RecorderDataPtr->numEvents += noOfSlots;
if( RecorderDataPtr->nextFreeIndex >= ( TRC_CFG_EVENT_BUFFER_SIZE ) )
{
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
/* We have reached the end, but this is a ring buffer. Start from the beginning again. */
RecorderDataPtr->bufferIsFull = 1;
RecorderDataPtr->nextFreeIndex = 0;
#else
/* We have reached the end so we stop. */
vTraceStop();
#endif
}
}
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
/* Make sure the next entry is cleared correctly */
prvCheckDataToBeOverwrittenForMultiEntryEvents( 1 );
#endif
}
}
trcCRITICAL_SECTION_END();
#elif ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 )
/* Use the separate user event buffer */
TraceStringHandle_t formatLabel;
traceUBChannel channel;
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
xTraceStringRegister( formatStr, &formatLabel );
channel = xTraceRegisterUBChannel( eventLabel, formatLabel );
prvTraceUBHelper1( channel, eventLabel, formatLabel, vl );
}
#endif /* if ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0 ) */
return TRC_SUCCESS;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
/******************************************************************************
* xTracePrint
*
* Basic user event
*
* Generates a User Event with a text label. The label is created/looked up
* in the symbol table using xTraceStringRegister.
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
traceResult xTracePrint( TraceStringHandle_t chn,
const char * str )
{
#if ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0 )
UserEvent * ue;
uint8_t dts1;
TRACE_ALLOC_CRITICAL_SECTION();
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
dts1 = ( uint8_t ) prvTraceGetDTS( 0xFF );
ue = ( UserEvent * ) prvTraceNextFreeEventBufferSlot();
if( ue != 0 )
{
ue->dts = dts1;
ue->type = USER_EVENT;
ue->payload = prvTraceOpenSymbol( str, chn );
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
#elif ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1 )
traceUBChannel channel;
uint32_t noOfSlots = 1;
uint32_t tempDataBuffer[ ( 3 + MAX_ARG_SIZE ) / 4 ];
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
TraceStringHandle_t trcStr = prvTraceOpenSymbol( str, chn );
channel = xTraceRegisterUBChannel( chn, trcStr );
if( channel == 0 )
{
/* We are dealing with an unknown channel format pair */
noOfSlots++; /* Also need room for channel and format */
( ( uint16_t * ) tempDataBuffer )[ 2 ] = chn;
( ( uint16_t * ) tempDataBuffer )[ 3 ] = trcStr;
}
prvTraceUBHelper2( channel, tempDataBuffer, noOfSlots );
}
#endif /* if ( TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0 ) */
return TRC_SUCCESS;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
/*******************************************************************************
* xTraceStringRegister
*
* Register strings in the recorder, e.g. for names of user event channels.
*
* Example:
* xTraceStringRegister("MyUserEvent", &myStringHandle);
* ...
* xTracePrintF(myEventHandle, "My value is: %d", myValue);
******************************************************************************/
#if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) )
traceResult xTraceStringRegister( const char * label,
TraceStringHandle_t * pxString )
{
TRACE_ASSERT( label != 0, "xTraceStringRegister: label == NULL", TRC_FAIL );
TRACE_ASSERT( RecorderDataPtr != 0, "Recorder not initialized, call vTraceEnable() first!", TRC_FAIL );
*pxString = prvTraceOpenSymbol( label, 0 );
return TRC_SUCCESS;
}
/* DEPRECATED */
TraceStringHandle_t xTraceRegisterString( const char * name )
{
TraceStringHandle_t trcStr = 0;
xTraceStringRegister( name, &trcStr );
return trcStr;
}
#endif /* if ( ( TRC_CFG_SCHEDULING_ONLY == 0 ) && ( TRC_CFG_INCLUDE_USER_EVENTS == 1 ) ) */
traceResult xTraceInitialize()
{
#if defined( TRC_CFG_ENABLE_STACK_MONITOR ) && ( TRC_CFG_ENABLE_STACK_MONITOR == 1 ) && ( TRC_CFG_SCHEDULING_ONLY == 0 )
uint32_t i;
#endif /* defined(TRC_CFG_ENABLE_STACK_MONITOR) && (TRC_CFG_ENABLE_STACK_MONITOR == 1) && (TRC_CFG_SCHEDULING_ONLY == 0) */
if( RecorderInitialized != 0 )
{
return TRC_SUCCESS;
}
/* These are set on init so they aren't overwritten by late initialization values. */
CurrentFilterMask = 0xFFFF;
CurrentFilterGroup = FilterGroup0;
traceErrorMessage = 0;
#if defined( TRC_CFG_ENABLE_STACK_MONITOR ) && ( TRC_CFG_ENABLE_STACK_MONITOR == 1 ) && ( TRC_CFG_SCHEDULING_ONLY == 0 )
tasksNotIncluded = 0;
for( i = 0; i < TRC_CFG_STACK_MONITOR_MAX_TASKS; i++ )
{
tasksInStackMonitor[ i ].tcb = 0;
tasksInStackMonitor[ i ].uiPreviousLowMark = 0;
}
#endif /* defined(TRC_CFG_ENABLE_STACK_MONITOR) && (TRC_CFG_ENABLE_STACK_MONITOR == 1) && (TRC_CFG_SCHEDULING_ONLY == 0) */
#if ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC )
RecorderDataPtr = &RecorderData;
#elif ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_DYNAMIC )
RecorderDataPtr = ( RecorderDataType * ) TRACE_MALLOC( sizeof( RecorderDataType ) );
if( !RecorderDataPtr )
{
prvTraceError( "Failed allocating recorder buffer!" );
return TRC_FAIL;
}
#elif ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM )
if( !RecorderDataPtr )
{
prvTraceError( "Recorder data pointer not set! Use vTraceSetRecorderDataBuffer()." );
return TRC_FAIL;
}
#endif /* if ( TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC ) */
init_hwtc_count = TRC_HWTC_COUNT;
if( xTraceKernelPortInitialize( &xKernelPortDataBuffer ) == TRC_FAIL )
{
return TRC_FAIL;
}
( void ) memset( RecorderDataPtr, 0, sizeof( RecorderDataType ) );
RecorderDataPtr->version = TRACE_KERNEL_VERSION;
RecorderDataPtr->minor_version = TRACE_MINOR_VERSION;
RecorderDataPtr->irq_priority_order = TRC_IRQ_PRIORITY_ORDER;
RecorderDataPtr->filesize = sizeof( RecorderDataType );
RecorderDataPtr->maxEvents = ( TRC_CFG_EVENT_BUFFER_SIZE );
RecorderDataPtr->debugMarker0 = ( int32_t ) 0xF0F0F0F0;
RecorderDataPtr->isUsing16bitHandles = TRC_CFG_USE_16BIT_OBJECT_HANDLES;
RecorderDataPtr->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD;
/* This function is kernel specific */
xTraceKernelPortInitObjectPropertyTable();
RecorderDataPtr->debugMarker1 = ( int32_t ) 0xF1F1F1F1;
RecorderDataPtr->SymbolTable.symTableSize = ( TRC_CFG_SYMBOL_TABLE_SIZE );
RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1;
#if ( TRC_CFG_INCLUDE_FLOAT_SUPPORT == 1 )
RecorderDataPtr->exampleFloatEncoding = 1.0f; /* otherwise already zero */
#endif
RecorderDataPtr->debugMarker2 = ( int32_t ) 0xF2F2F2F2;
prvStrncpy( RecorderDataPtr->systemInfo, "Trace Recorder Demo", 80 );
RecorderDataPtr->debugMarker3 = ( int32_t ) 0xF3F3F3F3;
RecorderDataPtr->endmarker0 = 0x0A;
RecorderDataPtr->endmarker1 = 0x0B;
RecorderDataPtr->endmarker2 = 0x0C;
RecorderDataPtr->endmarker3 = 0x0D;
RecorderDataPtr->endmarker4 = 0x71;
RecorderDataPtr->endmarker5 = 0x72;
RecorderDataPtr->endmarker6 = 0x73;
RecorderDataPtr->endmarker7 = 0x74;
RecorderDataPtr->endmarker8 = 0xF1;
RecorderDataPtr->endmarker9 = 0xF2;
RecorderDataPtr->endmarker10 = 0xF3;
RecorderDataPtr->endmarker11 = 0xF4;
#if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER
RecorderDataPtr->userEventBuffer.bufferID = 1;
RecorderDataPtr->userEventBuffer.version = 0;
RecorderDataPtr->userEventBuffer.numberOfSlots = ( TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE );
RecorderDataPtr->userEventBuffer.numberOfChannels = ( TRC_CFG_UB_CHANNELS ) +1;
#endif
/* Kernel specific initialization of the objectHandleStacks variable */
xTraceKernelPortInitObjectHandleStack();
/* Finally, the 12-byte "start markers" are initialized, allowing for
* Tracealyzer to find the trace data in a larger RAM dump.
*
* The start and end markers must be unique, but without proper precautions there
* might be a risk of accidental duplicates of the start/end markers, e.g., due to
* compiler optimizations.
*
* The below initialization of the start marker is therefore made in reverse order
* and the fields are volatile to ensure this assignment order. This to avoid any
* chance of accidental duplicates of this elsewhere in memory.
*
* Moreover, the fields are set byte-by-byte to avoid endian issues.*/
RecorderDataPtr->startmarker11 = 0xF4;
RecorderDataPtr->startmarker10 = 0xF3;
RecorderDataPtr->startmarker9 = 0xF2;
RecorderDataPtr->startmarker8 = 0xF1;
RecorderDataPtr->startmarker7 = 0x74;
RecorderDataPtr->startmarker6 = 0x73;
RecorderDataPtr->startmarker5 = 0x72;
RecorderDataPtr->startmarker4 = 0x71;
RecorderDataPtr->startmarker3 = 0x04;
RecorderDataPtr->startmarker2 = 0x03;
RecorderDataPtr->startmarker1 = 0x02;
RecorderDataPtr->startmarker0 = 0x01;
if( traceErrorMessage != 0 )
{
/* An error was detected before vTraceEnable was called, make sure this is stored in the trace data. */
prvStrncpy( RecorderDataPtr->systemInfo, traceErrorMessage, 80 );
RecorderDataPtr->internalErrorOccurred = 1;
prvTraceStop();
}
#ifdef TRC_PORT_SPECIFIC_INIT
TRC_PORT_SPECIFIC_INIT();
#endif
RecorderInitialized = 1;
return TRC_SUCCESS;
}
#if ( ( !defined TRC_CFG_INCLUDE_READY_EVENTS ) || ( TRC_CFG_INCLUDE_READY_EVENTS == 1 ) )
void prvTraceSetReadyEventsEnabled( uint32_t flag )
{
readyEventsEnabled = flag;
}
/*******************************************************************************
* prvTraceStoreTaskReady
*
* This function stores a ready state for the task handle sent in as parameter.
******************************************************************************/
void prvTraceStoreTaskReady( traceHandle handle )
{
uint16_t dts3;
TREvent * tr;
uint8_t hnd8;
TRACE_ALLOC_CRITICAL_SECTION();
if( handle == 0 )
{
/* On FreeRTOS v7.3.0, this occurs when creating tasks due to a bad
* placement of the trace macro. In that case, the events are ignored. */
return;
}
if( !readyEventsEnabled )
{
/* When creating tasks, ready events are also created. If creating
* a "hidden" (not traced) task, we must therefore disable recording
* of ready events to avoid an undesired ready event... */
return;
}
TRACE_ASSERT( handle <= ( TRC_CFG_NTASK ), "prvTraceStoreTaskReady: Invalid value for handle", TRC_UNUSED );
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "Recorder busy - high priority ISR using syscall? (1)" );
return;
}
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive ) /* Need to repeat this check! */
{
dts3 = ( uint16_t ) prvTraceGetDTS( 0xFFFF );
hnd8 = prvTraceGet8BitHandle( handle );
tr = ( TREvent * ) prvTraceNextFreeEventBufferSlot();
if( tr != 0 )
{
tr->type = DIV_TASK_READY;
tr->dts = dts3;
tr->objHandle = hnd8;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
#endif /* if ( ( !defined TRC_CFG_INCLUDE_READY_EVENTS ) || ( TRC_CFG_INCLUDE_READY_EVENTS == 1 ) ) */
/*******************************************************************************
* prvTraceStoreLowPower
*
* This function stores a low power state.
******************************************************************************/
void prvTraceStoreLowPower( uint32_t flag )
{
uint16_t dts;
LPEvent * lp;
TRACE_ALLOC_CRITICAL_SECTION();
TRACE_ASSERT( flag <= 1, "prvTraceStoreLowPower: Invalid flag value", TRC_UNUSED );
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "Recorder busy - high priority ISR using syscall? (1)" );
return;
}
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive )
{
dts = ( uint16_t ) prvTraceGetDTS( 0xFFFF );
lp = ( LPEvent * ) prvTraceNextFreeEventBufferSlot();
if( lp != 0 )
{
lp->type = ( uint8_t ) ( LOW_POWER_BEGIN + ( uint8_t ) flag ); /* BEGIN or END depending on flag */
lp->dts = dts;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
/*******************************************************************************
* vTraceStoreMemMangEvent
*
* This function stores malloc and free events. Each call requires two records,
* for size and address respectively. The event code parameter (ecode) is applied
* to the first record (size) and the following address record gets event
* code "ecode + 1", so make sure this is respected in the event code table.
* Note: On "free" calls, the signed_size parameter should be negative.
******************************************************************************/
#if ( TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1 )
#if ( TRC_CFG_SCHEDULING_ONLY == 0 )
void vTraceStoreMemMangEvent( uint32_t ecode,
uint32_t address,
int32_t signed_size )
{
uint8_t dts1;
MemEventSize * ms;
MemEventAddr * ma;
uint16_t size_low;
uint16_t addr_low;
uint8_t addr_high;
uint32_t size;
TRACE_ALLOC_CRITICAL_SECTION();
if( RecorderDataPtr == 0 )
{
/* Occurs in vTraceInitTraceData, if using dynamic allocation. */
return;
}
if( signed_size < 0 )
{
size = ( uint32_t ) ( -signed_size );
}
else
{
size = ( uint32_t ) ( signed_size );
}
trcCRITICAL_SECTION_BEGIN();
/* Only update heapMemUsage if we have a valid address */
if( address != 0 )
{
RecorderDataPtr->heapMemUsage += ( uint32_t ) signed_size;
if( RecorderDataPtr->heapMemUsage > RecorderDataPtr->heapMemMaxUsage )
{
RecorderDataPtr->heapMemMaxUsage = RecorderDataPtr->heapMemUsage;
}
}
if( RecorderDataPtr->recorderActive )
{
dts1 = ( uint8_t ) prvTraceGetDTS( 0xFF );
size_low = ( uint16_t ) prvTraceGetParam( 0xFFFF, size );
ms = ( MemEventSize * ) prvTraceNextFreeEventBufferSlot();
if( ms != 0 )
{
ms->dts = dts1;
ms->type = NULL_EVENT; /* Updated when all events are written */
ms->size = size_low;
prvTraceUpdateCounters();
/* Storing a second record with address (signals "failed" if null) */
#if ( TRC_CFG_HEAP_SIZE_BELOW_16M )
/* If the heap address range is within 16 MB, i.e., the upper 8 bits
* of addresses are constant, this optimization avoids storing an extra
* event record by ignoring the upper 8 bit of the address */
addr_low = address & 0xFFFF;
addr_high = ( address >> 16 ) & 0xFF;
#else
/* The whole 32 bit address is stored using a second event record
* for the upper 16 bit */
addr_low = ( uint16_t ) prvTraceGetParam( 0xFFFF, address );
addr_high = 0;
#endif
ma = ( MemEventAddr * ) prvTraceNextFreeEventBufferSlot();
if( ma != 0 )
{
ma->addr_low = addr_low;
ma->addr_high = addr_high;
ma->type = ( uint8_t ) ( ecode + 1 ); /* Note this! */
ms->type = ( uint8_t ) ecode; /* Set type of first event */
prvTraceUpdateCounters();
}
}
}
trcCRITICAL_SECTION_END();
}
#endif /* TRC_CFG_SCHEDULING_ONLY */
#endif /* if ( TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1 ) */
/*******************************************************************************
* prvTraceStoreKernelCall
*
* This is the main integration point for storing kernel calls, and
* is called by the hooks in trcKernelHooks.h (see trcKernelPort.h for event codes).
******************************************************************************/
#if ( TRC_CFG_SCHEDULING_ONLY == 0 )
void prvTraceStoreKernelCall( uint32_t ecode,
traceObjectClass objectClass,
uint32_t objectNumber )
{
KernelCall * kse;
uint16_t dts1;
uint8_t hnd8;
TRACE_ALLOC_CRITICAL_SECTION();
/* Avoids warnings when asserts are disabled */
( void ) objectClass;
TRACE_ASSERT( ecode < 0xFF, "prvTraceStoreKernelCall: ecode >= 0xFF", TRC_UNUSED );
TRACE_ASSERT( objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCall: objectClass >= TRACE_NCLASSES", TRC_UNUSED );
TRACE_ASSERT( objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectClass ], "prvTraceStoreKernelCall: Invalid value for objectNumber", TRC_UNUSED );
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "Recorder busy - high priority ISR using syscall? (2)" );
return;
}
if( handle_of_last_logged_task == 0 )
{
return;
}
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive )
{
dts1 = ( uint16_t ) prvTraceGetDTS( 0xFFFF );
hnd8 = prvTraceGet8BitHandle( ( traceHandle ) objectNumber );
kse = ( KernelCall * ) prvTraceNextFreeEventBufferSlot();
if( kse != 0 )
{
kse->dts = dts1;
kse->type = ( uint8_t ) ecode;
kse->objHandle = hnd8;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
#endif /* TRC_CFG_SCHEDULING_ONLY */
/*******************************************************************************
* prvTraceStoreKernelCallWithParam
*
* Used for storing kernel calls with a handle and a numeric parameter. If the
* numeric parameter does not fit in one byte, and extra XPS event is inserted
* before the kernel call event containing the three upper bytes.
******************************************************************************/
#if ( TRC_CFG_SCHEDULING_ONLY == 0 )
void prvTraceStoreKernelCallWithParam( uint32_t evtcode,
traceObjectClass objectClass,
uint32_t objectNumber,
uint32_t param )
{
KernelCallWithParamAndHandle * kse;
uint8_t dts2;
uint8_t hnd8;
uint8_t p8;
TRACE_ALLOC_CRITICAL_SECTION();
/* Avoids warnings when asserts are disabled */
( void ) objectClass;
TRACE_ASSERT( evtcode < 0xFF, "prvTraceStoreKernelCallWithParam: evtcode >= 0xFF", TRC_UNUSED );
TRACE_ASSERT( objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCallWithParam: objectClass >= TRACE_NCLASSES", TRC_UNUSED );
TRACE_ASSERT( objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectClass ], "prvTraceStoreKernelCallWithParam: Invalid value for objectNumber", TRC_UNUSED );
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "Recorder busy - high priority ISR using syscall? (3)" );
return;
}
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
dts2 = ( uint8_t ) prvTraceGetDTS( 0xFF );
p8 = ( uint8_t ) prvTraceGetParam( 0xFF, param );
hnd8 = prvTraceGet8BitHandle( ( traceHandle ) objectNumber );
kse = ( KernelCallWithParamAndHandle * ) prvTraceNextFreeEventBufferSlot();
if( kse != 0 )
{
kse->dts = dts2;
kse->type = ( uint8_t ) evtcode;
kse->objHandle = hnd8;
kse->param = p8;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
#endif /* TRC_CFG_SCHEDULING_ONLY */
/*******************************************************************************
* prvTraceGetParam
*
* Used for storing extra bytes for kernel calls with numeric parameters.
*
* May only be called within a critical section!
******************************************************************************/
#if ( TRC_CFG_SCHEDULING_ONLY == 0 )
static uint32_t prvTraceGetParam( uint32_t param_max,
uint32_t param )
{
XPSEvent * xps;
TRACE_ASSERT( param_max == 0xFF || param_max == 0xFFFF,
"prvTraceGetParam: Invalid value for param_max", param );
if( param <= param_max )
{
return param;
}
else
{
xps = ( XPSEvent * ) prvTraceNextFreeEventBufferSlot();
if( xps != 0 )
{
xps->type = DIV_XPS;
xps->xps_8 = ( uint8_t ) ( ( param & ( 0xFF00 & ~param_max ) ) >> 8 );
xps->xps_16 = ( uint16_t ) ( ( param & ( 0xFFFF0000 & ~param_max ) ) >> 16 );
prvTraceUpdateCounters();
}
return param & param_max;
}
}
#endif /* if ( TRC_CFG_SCHEDULING_ONLY == 0 ) */
/*******************************************************************************
* prvTraceStoreKernelCallWithNumericParamOnly
*
* Used for storing kernel calls with numeric parameters only. This is
* only used for traceTASK_DELAY and traceDELAY_UNTIL at the moment.
******************************************************************************/
#if ( TRC_CFG_SCHEDULING_ONLY == 0 )
void prvTraceStoreKernelCallWithNumericParamOnly( uint32_t evtcode,
uint32_t param )
{
KernelCallWithParam16 * kse;
uint8_t dts6;
uint16_t restParam;
TRACE_ALLOC_CRITICAL_SECTION();
restParam = 0;
TRACE_ASSERT( evtcode < 0xFF, "prvTraceStoreKernelCallWithNumericParamOnly: Invalid value for evtcode", TRC_UNUSED );
if( recorder_busy )
{
/*************************************************************************
* This occurs if an ISR calls a trace function, preempting a previous
* trace call that is being processed in a different ISR or task.
* If this occurs, there is probably a problem in the definition of the
* recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
* TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
* and any other ISRs that calls the trace recorder directly or via
* traced kernel functions. The ARM port disables all interrupts using the
* PRIMASK register to avoid this issue.
*************************************************************************/
prvTraceError( "Recorder busy - high priority ISR using syscall? (4)" );
return;
}
trcCRITICAL_SECTION_BEGIN();
if( RecorderDataPtr->recorderActive && handle_of_last_logged_task )
{
dts6 = ( uint8_t ) prvTraceGetDTS( 0xFF );
restParam = ( uint16_t ) prvTraceGetParam( 0xFFFF, param );
kse = ( KernelCallWithParam16 * ) prvTraceNextFreeEventBufferSlot();
if( kse != 0 )
{
kse->dts = dts6;
kse->type = ( uint8_t ) evtcode;
kse->param = restParam;
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END();
}
#endif /* TRC_CFG_SCHEDULING_ONLY */
/*******************************************************************************
* prvTraceStoreTaskswitch
* Called by the scheduler from the SWITCHED_OUT hook, and by uiTraceStart.
* At this point interrupts are assumed to be disabled!
******************************************************************************/
void prvTraceStoreTaskswitch( traceHandle task_handle )
{
uint16_t dts3;
TSEvent * ts;
uint8_t hnd8;
#if ( TRC_CFG_INCLUDE_ISR_TRACING == 1 )
extern int32_t isPendingContextSwitch;
#endif
trcSR_ALLOC_CRITICAL_SECTION_ON_CORTEX_M_ONLY();
TRACE_ASSERT( task_handle <= ( TRC_CFG_NTASK ),
"prvTraceStoreTaskswitch: Invalid value for task_handle", TRC_UNUSED );
trcCRITICAL_SECTION_BEGIN_ON_CORTEX_M_ONLY();
if( ( task_handle != handle_of_last_logged_task ) && ( RecorderDataPtr->recorderActive ) )
{
#if ( TRC_CFG_INCLUDE_ISR_TRACING == 1 )
isPendingContextSwitch = 0;
#endif
dts3 = ( uint16_t ) prvTraceGetDTS( 0xFFFF );
handle_of_last_logged_task = task_handle;
hnd8 = prvTraceGet8BitHandle( handle_of_last_logged_task );
ts = ( TSEvent * ) prvTraceNextFreeEventBufferSlot();
if( ts != 0 )
{
if( prvTraceGetObjectState( TRACE_CLASS_TASK,
handle_of_last_logged_task ) == TASK_STATE_INSTANCE_ACTIVE )
{
ts->type = TS_TASK_RESUME;
}
else
{
ts->type = TS_TASK_BEGIN;
}
ts->dts = dts3;
ts->objHandle = hnd8;
prvTraceSetObjectState( TRACE_CLASS_TASK,
handle_of_last_logged_task,
TASK_STATE_INSTANCE_ACTIVE );
prvTraceUpdateCounters();
}
}
trcCRITICAL_SECTION_END_ON_CORTEX_M_ONLY();
}
/*******************************************************************************
* prvTraceStoreObjectNameOnCloseEvent
*
* Updates the symbol table with the name of this object from the dynamic
* objects table and stores a "close" event, holding the mapping between handle
* and name (a symbol table handle). The stored name-handle mapping is thus the
* "old" one, valid up until this point.
******************************************************************************/
void prvTraceStoreObjectNameOnCloseEvent( uint8_t evtcode,
traceHandle handle,
traceObjectClass objectclass )
{
ObjCloseNameEvent * ce;
const char * name;
TraceStringHandle_t idx;
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceStoreObjectNameOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED );
TRACE_ASSERT( handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceStoreObjectNameOnCloseEvent: Invalid value for handle", TRC_UNUSED );
if( RecorderDataPtr->recorderActive )
{
uint8_t hnd8 = prvTraceGet8BitHandle( handle );
name = TRACE_PROPERTY_NAME_GET( objectclass, handle );
idx = prvTraceOpenSymbol( name, 0 );
/* Interrupt disable not necessary, already done in trcHooks.h macro */
ce = ( ObjCloseNameEvent * ) prvTraceNextFreeEventBufferSlot();
if( ce != 0 )
{
ce->type = ( uint8_t ) evtcode;
ce->objHandle = hnd8;
ce->symbolIndex = idx;
prvTraceUpdateCounters();
}
}
}
void prvTraceStoreObjectPropertiesOnCloseEvent( uint8_t evtcode,
traceHandle handle,
traceObjectClass objectclass )
{
ObjClosePropEvent * pe;
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceStoreObjectPropertiesOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED );
TRACE_ASSERT( handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceStoreObjectPropertiesOnCloseEvent: Invalid value for handle", TRC_UNUSED );
if( RecorderDataPtr->recorderActive )
{
/* Interrupt disable not necessary, already done in trcHooks.h macro */
pe = ( ObjClosePropEvent * ) prvTraceNextFreeEventBufferSlot();
if( pe != 0 )
{
if( objectclass == TRACE_CLASS_TASK )
{
pe->arg1 = TRACE_PROPERTY_ACTOR_PRIORITY( objectclass, handle );
}
else
{
pe->arg1 = TRACE_PROPERTY_OBJECT_STATE( objectclass, handle );
}
pe->type = evtcode;
prvTraceUpdateCounters();
}
}
}
void prvTraceSetPriorityProperty( uint8_t objectclass,
traceHandle id,
uint8_t value )
{
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceSetPriorityProperty: objectclass >= TRACE_NCLASSES", TRC_UNUSED );
TRACE_ASSERT( id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceSetPriorityProperty: Invalid value for id", TRC_UNUSED );
TRACE_PROPERTY_ACTOR_PRIORITY( objectclass, id ) = value;
}
uint8_t prvTraceGetPriorityProperty( uint8_t objectclass,
traceHandle id )
{
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceGetPriorityProperty: objectclass >= TRACE_NCLASSES", 0 );
TRACE_ASSERT( id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceGetPriorityProperty: Invalid value for id", 0 );
return TRACE_PROPERTY_ACTOR_PRIORITY( objectclass, id );
}
void prvTraceSetObjectState( uint8_t objectclass,
traceHandle id,
uint8_t value )
{
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceSetObjectState: objectclass >= TRACE_NCLASSES", TRC_UNUSED );
TRACE_ASSERT( id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceSetObjectState: Invalid value for id", TRC_UNUSED );
TRACE_PROPERTY_OBJECT_STATE( objectclass, id ) = value;
}
uint8_t prvTraceGetObjectState( uint8_t objectclass,
traceHandle id )
{
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceGetObjectState: objectclass >= TRACE_NCLASSES", 0 );
TRACE_ASSERT( id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceGetObjectState: Invalid value for id", 0 );
return TRACE_PROPERTY_OBJECT_STATE( objectclass, id );
}
void prvTraceSetTaskInstanceFinished( traceHandle handle )
{
/* Avoids warnings when asserts are disabled */
( void ) handle;
TRACE_ASSERT( handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ TRACE_CLASS_TASK ],
"prvTraceSetTaskInstanceFinished: Invalid value for handle", TRC_UNUSED );
#if ( TRC_CFG_USE_IMPLICIT_IFE_RULES == 1 )
TRACE_PROPERTY_OBJECT_STATE( TRACE_CLASS_TASK, handle ) = 0;
#endif
}
void * prvTraceNextFreeEventBufferSlot( void )
{
if( !RecorderDataPtr->recorderActive )
{
/* If an XTS or XPS event prior to the main event has filled the buffer
* before saving the main event, and store mode is "stop when full". */
return 0;
}
if( RecorderDataPtr->nextFreeIndex >= ( TRC_CFG_EVENT_BUFFER_SIZE ) )
{
prvTraceError( "Attempt to index outside event buffer!" );
return 0;
}
return ( void * ) ( &RecorderDataPtr->eventData[ RecorderDataPtr->nextFreeIndex * 4 ] );
}
uint16_t uiIndexOfObject( traceHandle objecthandle,
uint8_t objectclass )
{
uint16_t index;
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"uiIndexOfObject: Invalid value for objectclass", 0 );
TRACE_ASSERT( objecthandle > 0 && objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"uiIndexOfObject: Invalid value for objecthandle", 0 );
if( ( objectclass < TRACE_NCLASSES ) && ( objecthandle > 0 ) &&
( objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ] ) )
{
index = ( uint16_t ) ( RecorderDataPtr->ObjectPropertyTable.StartIndexOfClass[ objectclass ] +
( RecorderDataPtr->ObjectPropertyTable.TotalPropertyBytesPerClass[ objectclass ] * ( objecthandle - 1 ) ) );
return index;
}
prvTraceError( "Object table lookup with invalid object handle or object class!" );
return 0;
}
traceHandle prvTraceGetObjectHandle( traceObjectClass objectclass )
{
traceHandle handle;
static int indexOfHandle;
TRACE_ALLOC_CRITICAL_SECTION();
TRACE_ASSERT( RecorderDataPtr != 0, "Recorder not initialized, call vTraceEnable() first!", ( traceHandle ) 0 );
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceGetObjectHandle: Invalid value for objectclass", ( traceHandle ) 0 );
trcCRITICAL_SECTION_BEGIN();
indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[ objectclass ];
if( objectHandleStacks.objectHandles[ indexOfHandle ] == 0 )
{
/* Zero is used to indicate a never before used handle, i.e.,
* new slots in the handle stack. The handle slot needs to
* be initialized here (starts at 1). */
objectHandleStacks.objectHandles[ indexOfHandle ] =
( traceHandle ) ( 1 + indexOfHandle -
objectHandleStacks.lowestIndexOfClass[ objectclass ] );
}
handle = objectHandleStacks.objectHandles[ indexOfHandle ];
if( objectHandleStacks.indexOfNextAvailableHandle[ objectclass ]
> objectHandleStacks.highestIndexOfClass[ objectclass ] )
{
prvTraceError( pszTraceGetErrorNotEnoughHandles( objectclass ) );
handle = 0;
}
else
{
int hndCount;
objectHandleStacks.indexOfNextAvailableHandle[ objectclass ]++;
hndCount = objectHandleStacks.indexOfNextAvailableHandle[ objectclass ] -
objectHandleStacks.lowestIndexOfClass[ objectclass ];
if( hndCount >
objectHandleStacks.handleCountWaterMarksOfClass[ objectclass ] )
{
objectHandleStacks.handleCountWaterMarksOfClass[ objectclass ] =
( traceHandle ) hndCount;
}
}
trcCRITICAL_SECTION_END();
return handle;
}
void prvTraceFreeObjectHandle( traceObjectClass objectclass,
traceHandle handle )
{
int indexOfHandle;
TRACE_ASSERT( RecorderDataPtr != 0, "Recorder not initialized, call vTraceEnable() first!", TRC_UNUSED );
TRACE_ASSERT( objectclass < TRACE_NCLASSES,
"prvTraceFreeObjectHandle: Invalid value for objectclass", TRC_UNUSED );
TRACE_ASSERT( handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ],
"prvTraceFreeObjectHandle: Invalid value for handle", TRC_UNUSED );
/* Check that there is room to push the handle on the stack */
if( ( objectHandleStacks.indexOfNextAvailableHandle[ objectclass ] - 1 ) <
objectHandleStacks.lowestIndexOfClass[ objectclass ] )
{
/* Error */
prvTraceError( "Attempt to free more handles than allocated!" );
}
else
{
objectHandleStacks.indexOfNextAvailableHandle[ objectclass ]--;
indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[ objectclass ];
objectHandleStacks.objectHandles[ indexOfHandle ] = handle;
}
}
/*******************************************************************************
* prvMarkObjectAsUsed
*
* Sets an "is used flag" on object creation, using the first byte of the name
* field. This allows for counting the number of used Object Table slots, even
* if no names have been set.
******************************************************************************/
void prvMarkObjectAsUsed( traceObjectClass objectclass,
traceHandle handle )
{
uint16_t idx = uiIndexOfObject( handle, objectclass );
RecorderDataPtr->ObjectPropertyTable.objbytes[ idx ] = 1;
}
/*******************************************************************************
* prvStrncpy
*
* Private string copy function, to improve portability between compilers.
******************************************************************************/
static void prvStrncpy( char * dst,
const char * src,
uint32_t maxLength )
{
uint32_t i;
for( i = 0; i < maxLength; i++ )
{
dst[ i ] = src[ i ];
if( src[ i ] == 0 )
{
break;
}
}
}
/*******************************************************************************
* prvTraceSetObjectName
*
* Registers the names of queues, semaphores and other kernel objects in the
* recorder's Object Property Table, at the given handle and object class.
******************************************************************************/
void prvTraceSetObjectName( traceObjectClass objectclass,
traceHandle handle,
const char * name )
{
static uint16_t idx;
if( name == 0 )
{
name = "";
}
if( objectclass >= TRACE_NCLASSES )
{
prvTraceError( "Illegal object class in prvTraceSetObjectName" );
return;
}
if( handle == 0 )
{
prvTraceError( "Illegal handle (0) in prvTraceSetObjectName." );
return;
}
if( handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[ objectclass ] )
{
/* ERROR */
prvTraceError( pszTraceGetErrorNotEnoughHandles( objectclass ) );
}
else
{
idx = uiIndexOfObject( handle, objectclass );
if( traceErrorMessage == 0 )
{
prvStrncpy( ( char * ) &( RecorderDataPtr->ObjectPropertyTable.objbytes[ idx ] ),
name,
RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ] );
}
}
}
TraceStringHandle_t prvTraceOpenSymbol( const char * name,
TraceStringHandle_t userEventChannel )
{
uint16_t result;
uint8_t len;
uint8_t crc;
TRACE_ALLOC_CRITICAL_SECTION();
len = 0;
crc = 0;
TRACE_ASSERT( name != 0, "prvTraceOpenSymbol: name == NULL", ( TraceStringHandle_t ) 0 );
prvTraceGetChecksum( name, &crc, &len );
trcCRITICAL_SECTION_BEGIN();
result = prvTraceLookupSymbolTableEntry( name, crc, len, userEventChannel );
if( !result )
{
result = prvTraceCreateSymbolTableEntry( name, crc, len, userEventChannel );
}
trcCRITICAL_SECTION_END();
return result;
}
/******************************************************************************
* vTraceSetFrequency
*
* Registers the clock rate of the time source for the event timestamping.
* This is normally not required, but if the default value (TRC_HWTC_FREQ_HZ)
* should be incorrect for your setup, you can override it using this function.
*
* Must be called prior to vTraceEnable, and the time source is assumed to
* have a fixed clock frequency after the startup.
*
* Note that, in snapshot mode, the value is divided by the TRC_HWTC_DIVISOR.
* This is a software "prescaler" that is also applied on the timestamps.
*****************************************************************************/
void vTraceSetFrequency( uint32_t frequency )
{
timestampFrequency = frequency;
}
/*******************************************************************************
* Supporting functions
******************************************************************************/
/*******************************************************************************
* prvTraceError
*
* Called by various parts in the recorder. Stops the recorder and stores a
* pointer to an error message, which is printed by the monitor task.
* If you are not using the monitor task, you may use xTraceErrorGetLast()
* from your application to check if the recorder is OK.
*
* Note: If a recorder error is registered before vTraceStart is called, the
* trace start will be aborted. This can occur if any of the Nxxxx constants
* (e.g., TRC_CFG_NTASK) in trcConfig.h is too small.
******************************************************************************/
void prvTraceError( const char * msg )
{
/* Stop the recorder */
if( RecorderDataPtr != 0 )
{
xTraceDisable();
}
/* If first error only... */
if( traceErrorMessage == 0 )
{
traceErrorMessage = ( char * ) ( intptr_t ) msg;
if( RecorderDataPtr != 0 )
{
prvStrncpy( RecorderDataPtr->systemInfo, traceErrorMessage, 80 );
RecorderDataPtr->internalErrorOccurred = 1;
}
}
}
void vTraceSetFilterMask( uint16_t filterMask )
{
CurrentFilterMask = filterMask;
}
void vTraceSetFilterGroup( uint16_t filterGroup )
{
CurrentFilterGroup = filterGroup;
}
/******************************************************************************
* prvCheckDataToBeOverwrittenForMultiEntryEvents
*
* This checks if the next event to be overwritten is a multi-entry user event,
* i.e., a USER_EVENT followed by data entries.
* Such data entries do not have an event code at byte 0, as other events.
* All 4 bytes are user data, so the first byte of such data events must
* not be interpreted as type field. The number of data entries following
* a USER_EVENT is given in the event code of the USER_EVENT.
* Therefore, when overwriting a USER_EVENT (when using in ring-buffer mode)
* any data entries following must be replaced with NULL events (code 0).
*
* This is assumed to execute within a critical section...
*****************************************************************************/
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
void prvCheckDataToBeOverwrittenForMultiEntryEvents( uint8_t nofEntriesToCheck )
{
/* Generic "int" type is desired - should be 16 bit variable on 16 bit HW */
unsigned int i = 0;
unsigned int e = 0;
TRACE_ASSERT( nofEntriesToCheck != 0,
"prvCheckDataToBeOverwrittenForMultiEntryEvents: nofEntriesToCheck == 0", TRC_UNUSED );
while( i < nofEntriesToCheck )
{
e = RecorderDataPtr->nextFreeIndex + i;
if( ( RecorderDataPtr->eventData[ e * 4 ] > USER_EVENT ) &&
( RecorderDataPtr->eventData[ e * 4 ] < USER_EVENT + 16 ) )
{
uint8_t nDataEvents = ( uint8_t ) ( RecorderDataPtr->eventData[ e * 4 ] - USER_EVENT );
if( ( e + nDataEvents ) < RecorderDataPtr->maxEvents )
{
( void ) memset( &RecorderDataPtr->eventData[ e * 4 ], 0, ( size_t ) ( 4 + 4 * nDataEvents ) );
}
}
else if( RecorderDataPtr->eventData[ e * 4 ] == DIV_XPS )
{
if( ( e + 1 ) < RecorderDataPtr->maxEvents )
{
/* Clear 8 bytes */
( void ) memset( &RecorderDataPtr->eventData[ e * 4 ], 0, 4 + 4 );
}
else
{
/* Clear 8 bytes, 4 first and 4 last */
( void ) memset( &RecorderDataPtr->eventData[ 0 ], 0, 4 );
( void ) memset( &RecorderDataPtr->eventData[ e * 4 ], 0, 4 );
}
}
i++;
}
}
#endif /* if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER ) */
/*******************************************************************************
* prvTraceUpdateCounters
*
* Updates the index of the event buffer.
******************************************************************************/
void prvTraceUpdateCounters( void )
{
if( RecorderDataPtr->recorderActive == 0 )
{
return;
}
RecorderDataPtr->numEvents++;
RecorderDataPtr->nextFreeIndex++;
if( RecorderDataPtr->nextFreeIndex >= ( TRC_CFG_EVENT_BUFFER_SIZE ) )
{
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
RecorderDataPtr->bufferIsFull = 1;
RecorderDataPtr->nextFreeIndex = 0;
#else
vTraceStop();
#endif
}
#if ( TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER )
prvCheckDataToBeOverwrittenForMultiEntryEvents( 1 );
#endif
}
/******************************************************************************
* prvTraceGetDTS
*
* Returns a differential timestamp (DTS), i.e., the time since
* last event, and creates an XTS event if the DTS does not fit in the
* number of bits given. The XTS event holds the MSB bytes of the DTS.
*
* The parameter param_maxDTS should be 0xFF for 8-bit dts or 0xFFFF for
* events with 16-bit dts fields.
*****************************************************************************/
uint16_t prvTraceGetDTS( uint16_t param_maxDTS )
{
static uint32_t old_timestamp = 0;
XTSEvent * xts = 0;
uint32_t dts = 0;
uint32_t timestamp = 0;
TRACE_ASSERT( param_maxDTS == 0xFF || param_maxDTS == 0xFFFF, "prvTraceGetDTS: Invalid value for param_maxDTS", 0 );
if( RecorderDataPtr->frequency == 0 )
{
if( timestampFrequency != 0 )
{
/* If to override default TRC_HWTC_FREQ_HZ value with value set by vTraceSetFrequency */
RecorderDataPtr->frequency = timestampFrequency / ( TRC_HWTC_DIVISOR );
}
else if( init_hwtc_count != ( TRC_HWTC_COUNT ) )
{
/* If using default value and timer has been started.
* Note: If the default frequency value set here would be incorrect, e.g.,
* if the timer has actually not been configured yet, override this
* with vTraceSetFrequency.
*/
RecorderDataPtr->frequency = ( TRC_HWTC_FREQ_HZ ) / ( TRC_HWTC_DIVISOR );
}
/* If no override (vTraceSetFrequency) and timer inactive -> no action */
}
/**************************************************************************
* The below statements read the timestamp from the timer port module.
* If necessary, whole seconds are extracted using division while the rest
* comes from the modulo operation.
**************************************************************************/
prvTracePortGetTimeStamp( &timestamp );
/***************************************************************************
* Since dts is unsigned the result will be correct even if timestamp has
* wrapped around.
***************************************************************************/
dts = timestamp - old_timestamp;
old_timestamp = timestamp;
if( RecorderDataPtr->frequency > 0 )
{
/* Check if dts > 1 second */
if( dts > RecorderDataPtr->frequency )
{
/* More than 1 second has passed */
RecorderDataPtr->absTimeLastEventSecond += dts / RecorderDataPtr->frequency;
/* The part that is not an entire second is added to absTimeLastEvent */
RecorderDataPtr->absTimeLastEvent += dts % RecorderDataPtr->frequency;
}
else
{
RecorderDataPtr->absTimeLastEvent += dts;
}
/* Check if absTimeLastEvent >= 1 second */
if( RecorderDataPtr->absTimeLastEvent >= RecorderDataPtr->frequency )
{
/* RecorderDataPtr->absTimeLastEvent is more than or equal to 1 second, but always less than 2 seconds */
RecorderDataPtr->absTimeLastEventSecond++;
RecorderDataPtr->absTimeLastEvent -= RecorderDataPtr->frequency;
/* RecorderDataPtr->absTimeLastEvent is now less than 1 second */
}
}
else
{
/* Special case if the recorder has not yet started (frequency may be uninitialized, i.e., zero) */
RecorderDataPtr->absTimeLastEvent = timestamp;
}
/* If the dts (time since last event) does not fit in event->dts (only 8 or 16 bits) */
if( dts > param_maxDTS )
{
/* Create an XTS event (eXtended TimeStamp) containing the higher dts bits*/
xts = ( XTSEvent * ) prvTraceNextFreeEventBufferSlot();
if( xts != 0 )
{
if( param_maxDTS == 0xFFFF )
{
xts->type = XTS16;
xts->xts_16 = ( uint16_t ) ( ( dts / 0x10000 ) & 0xFFFF );
xts->xts_8 = 0;
}
else if( param_maxDTS == 0xFF )
{
xts->type = XTS8;
xts->xts_16 = ( uint16_t ) ( ( dts / 0x100 ) & 0xFFFF );
xts->xts_8 = ( uint8_t ) ( ( dts / 0x1000000 ) & 0xFF );
}
else
{
prvTraceError( "Bad param_maxDTS in prvTraceGetDTS" );
}
prvTraceUpdateCounters();
}
}
return ( uint16_t ) dts & param_maxDTS;
}
/*******************************************************************************
* prvTraceLookupSymbolTableEntry
*
* Find an entry in the symbol table, return 0 if not present.
*
* The strings are stored in a byte pool, with four bytes of "meta-data" for
* every string.
* byte 0-1: index of next entry with same checksum (for fast lookup).
* byte 2-3: reference to a symbol table entry, a label for xTracePrintF
* format strings only (the handle of the destination channel).
* byte 4..(4 + length): the string (object name or user event label), with
* zero-termination
******************************************************************************/
TraceStringHandle_t prvTraceLookupSymbolTableEntry( const char * name,
uint8_t crc6,
uint8_t len,
TraceStringHandle_t chn )
{
uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ];
TRACE_ASSERT( name != 0, "prvTraceLookupSymbolTableEntry: name == NULL", ( TraceStringHandle_t ) 0 );
TRACE_ASSERT( len != 0, "prvTraceLookupSymbolTableEntry: len == 0", ( TraceStringHandle_t ) 0 );
while( i != 0 )
{
if( RecorderDataPtr->SymbolTable.symbytes[ i + 2 ] == ( chn & 0x00FF ) )
{
if( RecorderDataPtr->SymbolTable.symbytes[ i + 3 ] == ( chn / 0x100 ) )
{
if( RecorderDataPtr->SymbolTable.symbytes[ i + 4 + len ] == '\0' )
{
if( strncmp( ( char * ) ( &RecorderDataPtr->SymbolTable.symbytes[ i + 4 ] ), name, len ) == 0 )
{
break; /* found */
}
}
}
}
i = ( uint16_t ) ( RecorderDataPtr->SymbolTable.symbytes[ i ] + ( RecorderDataPtr->SymbolTable.symbytes[ i + 1 ] * 0x100 ) );
}
return i;
}
/*******************************************************************************
* prvTraceCreateSymbolTableEntry
*
* Creates an entry in the symbol table, independent if it exists already.
*
* The strings are stored in a byte pool, with four bytes of "meta-data" for
* every string.
* byte 0-1: index of next entry with same checksum (for fast lookup).
* byte 2-3: reference to a symbol table entry, a label for xTracePrintF
* format strings only (the handle of the destination channel).
* byte 4..(4 + length): the string (object name or user event label), with
* zero-termination
******************************************************************************/
TraceStringHandle_t prvTraceCreateSymbolTableEntry( const char * name,
uint8_t crc6,
uint8_t len,
TraceStringHandle_t channel )
{
TraceStringHandle_t ret = 0;
TRACE_ASSERT( name != 0, "prvTraceCreateSymbolTableEntry: name == NULL", 0 );
TRACE_ASSERT( len != 0, "prvTraceCreateSymbolTableEntry: len == 0", 0 );
if( RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + len + 4 >= ( TRC_CFG_SYMBOL_TABLE_SIZE ) )
{
prvTraceError( "Symbol table full. Increase TRC_CFG_SYMBOL_TABLE_SIZE in trcConfig.h" );
ret = 0;
}
else
{
RecorderDataPtr->SymbolTable.symbytes
[ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex ] =
( uint8_t ) ( RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] & 0x00FF );
RecorderDataPtr->SymbolTable.symbytes
[ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 1 ] =
( uint8_t ) ( RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] / 0x100 );
RecorderDataPtr->SymbolTable.symbytes
[ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 2 ] =
( uint8_t ) ( channel & 0x00FF );
RecorderDataPtr->SymbolTable.symbytes
[ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 3 ] =
( uint8_t ) ( channel / 0x100 );
/* set name (bytes 4...4+len-1) */
prvStrncpy( ( char * ) &( RecorderDataPtr->SymbolTable.symbytes
[ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 ] ), name, len );
/* Set zero termination (at offset 4+len) */
RecorderDataPtr->SymbolTable.symbytes
[ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 + len ] = '\0';
/* store index of entry (for return value, and as head of LL[crc6]) */
RecorderDataPtr->SymbolTable.latestEntryOfChecksum
[ crc6 ] = ( uint16_t ) RecorderDataPtr->SymbolTable.nextFreeSymbolIndex;
RecorderDataPtr->SymbolTable.nextFreeSymbolIndex += ( uint32_t ) ( len + 5 );
ret = ( uint16_t ) ( RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - ( uint8_t ) ( len + 5 ) );
}
return ret;
}
/*******************************************************************************
* prvTraceGetChecksum
*
* Calculates a simple 6-bit checksum from a string, used to index the string
* for fast symbol table lookup.
******************************************************************************/
void prvTraceGetChecksum( const char * pname,
uint8_t * pcrc,
uint8_t * plength )
{
unsigned char c;
int length = 1; /* Should be 1 to account for '\0' */
int crc = 0;
TRACE_ASSERT( pname != 0, "prvTraceGetChecksum: pname == NULL", TRC_UNUSED );
TRACE_ASSERT( pcrc != 0, "prvTraceGetChecksum: pcrc == NULL", TRC_UNUSED );
TRACE_ASSERT( plength != 0, "prvTraceGetChecksum: plength == NULL", TRC_UNUSED );
if( pname != ( const char * ) 0 )
{
for( ; ( c = ( unsigned char ) *pname++ ) != '\0'; )
{
crc += c;
length++;
}
}
*pcrc = ( uint8_t ) ( crc & 0x3F );
*plength = ( uint8_t ) length;
}
#if ( TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1 )
static void prvTraceStoreXID( traceHandle handle );
/******************************************************************************
* prvTraceStoreXID
*
* Stores an XID (eXtended IDentifier) event.
* This is used if an object/task handle is larger than 255.
* The parameter "handle" is the full (16 bit) handle, assumed to be 256 or
* larger. Handles below 256 should not use this function.
*
* NOTE: this function MUST be called from within a critical section.
*****************************************************************************/
static void prvTraceStoreXID( traceHandle handle )
{
XPSEvent * xid;
TRACE_ASSERT( handle >= 256, "prvTraceStoreXID: Handle < 256", TRC_UNUSED );
xid = ( XPSEvent * ) prvTraceNextFreeEventBufferSlot();
if( xid != 0 )
{
xid->type = XID;
/* This function is (only) used when traceHandle is 16 bit... */
xid->xps_16 = handle;
prvTraceUpdateCounters();
}
}
static uint8_t prvTraceGet8BitHandle( traceHandle handle )
{
if( handle > 255 )
{
prvTraceStoreXID( handle );
/* The full handle (16 bit) is stored in the XID event.
* This code (255) is used instead of zero (which is an error code).*/
return 255;
}
return ( uint8_t ) ( handle & 0xFF );
}
#endif /*(TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)*/
/* If using DWT timestamping (default on ARM Cortex-M3, M4 and M7), make sure the DWT unit is initialized. */
#ifndef TRC_CFG_ARM_CM_USE_SYSTICK
#if ( ( TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M ) && ( defined( __CORTEX_M ) && ( __CORTEX_M >= 0x03 ) ) )
void xTraceHardwarePortInitCortexM()
{
/* Ensure that the DWT registers are unlocked and can be modified. */
TRC_REG_ITM_LOCKACCESS = TRC_ITM_LOCKACCESS_UNLOCK;
/* Make sure DWT is enabled, if supported */
TRC_REG_DEMCR |= TRC_DEMCR_TRCENA;
do
{
/* Verify that DWT is supported */
if( TRC_REG_DEMCR == 0 )
{
/* This function is called on Cortex-M3, M4 and M7 devices to initialize
* the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
*
* If the below error is produced, the DWT unit does not seem to be available.
*
* In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
* to use SysTick timestamping instead, or define your own timestamping by
* setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
* and make the necessary definitions, as explained in trcHardwarePort.h.*/
prvTraceError( "DWT unit not available, see code comment." );
break;
}
/* Verify that DWT_CYCCNT is supported */
if( TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT )
{
/* This function is called on Cortex-M3, M4 and M7 devices to initialize
* the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
*
* If the below error is produced, the cycle counter does not seem to be available.
*
* In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
* to use SysTick timestamping instead, or define your own timestamping by
* setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
* and make the necessary definitions, as explained in trcHardwarePort.h.*/
prvTraceError( "DWT_CYCCNT not available, see code comment." );
break;
}
/* Reset the cycle counter */
TRC_REG_DWT_CYCCNT = 0;
/* Enable the cycle counter */
TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA;
} while( 0 ); /* breaks above jump here */
}
#endif /* if ( ( TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M ) && ( defined( __CORTEX_M ) && ( __CORTEX_M >= 0x03 ) ) ) */
#endif /* ifndef TRC_CFG_ARM_CM_USE_SYSTICK */
/******************************************************************************
* prvTracePortGetTimeStamp
*
* Returns the current time based on the HWTC macros which provide a hardware
* isolation layer towards the hardware timer/counter.
*
* The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
* or the trace recorder library. Typically you should not need to change
* the code of prvTracePortGetTimeStamp if using the HWTC macros.
*
******************************************************************************/
void prvTracePortGetTimeStamp( uint32_t * pTimestamp )
{
static uint32_t last_hwtc_count = 0;
uint32_t hwtc_count = 0;
#if TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR
/* systick based timer */
static uint32_t last_traceTickCount = 0;
uint32_t traceTickCount = 0;
#else /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
/* Free running timer */
static uint32_t last_hwtc_rest = 0;
uint32_t diff = 0;
uint32_t diff_scaled = 0;
#endif /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
if( trace_disable_timestamp == 1 )
{
if( pTimestamp )
{
*pTimestamp = last_timestamp;
}
return;
}
/* Retrieve TRC_HWTC_COUNT only once since the same value should be used all throughout this function. */
#if ( TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR )
/* Get the increasing tick count */
hwtc_count = ( TRC_HWTC_COUNT );
#elif ( TRC_HWTC_TYPE == TRC_OS_TIMER_DECR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR )
/* Convert decreasing tick count into increasing tick count */
hwtc_count = ( TRC_HWTC_PERIOD ) -( TRC_HWTC_COUNT );
#else
#error "TRC_HWTC_TYPE has unexpected value"
#endif
#if ( TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_Win32 )
/* The Win32 port uses ulGetRunTimeCounterValue for timestamping, which in turn
* uses QueryPerformanceCounter. That function is not always reliable when used over
* multiple threads. We must therefore handle rare cases where the timestamp is less
* than the previous. In practice, this should "never" roll over since the
* performance counter is 64 bit wide. */
if( last_hwtc_count > hwtc_count )
{
hwtc_count = last_hwtc_count;
}
#endif
#if ( TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR )
/* Timestamping is based on a timer that wraps at TRC_HWTC_PERIOD */
if( last_traceTickCount - uiTraceTickCount - 1 < 0x80000000 )
{
/* This means last_traceTickCount is higher than uiTraceTickCount,
* so we have previously compensated for a missed tick.
* Therefore we use the last stored value because that is more accurate. */
traceTickCount = last_traceTickCount;
}
else
{
/* Business as usual */
traceTickCount = uiTraceTickCount;
}
/* Check for overflow. May occur if the update of uiTraceTickCount has been
* delayed due to disabled interrupts. */
if( ( traceTickCount == last_traceTickCount ) && ( hwtc_count < last_hwtc_count ) )
{
/* A trace tick has occurred but not been executed by the kernel, so we compensate manually. */
traceTickCount++;
}
/* Check if the return address is OK, then we perform the calculation. */
if( pTimestamp )
{
/* Get timestamp from trace ticks. Scale down the period to avoid unwanted overflows. */
last_timestamp = traceTickCount * ( ( TRC_HWTC_PERIOD ) / ( TRC_HWTC_DIVISOR ) );
/* Increase timestamp by (hwtc_count + "lost hardware ticks from scaling down period") / TRC_HWTC_DIVISOR. */
last_timestamp += ( hwtc_count + traceTickCount * ( ( TRC_HWTC_PERIOD ) % ( TRC_HWTC_DIVISOR ) ) ) / ( TRC_HWTC_DIVISOR );
}
/* Store the previous value */
last_traceTickCount = traceTickCount;
#else /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
/* Timestamping is based on a free running timer */
/* This part handles free running clocks that can be scaled down to avoid too large DTS values.
* Without this, the scaled timestamp will incorrectly wrap at (2^32 / TRC_HWTC_DIVISOR) ticks.
* The scaled timestamp returned from this function is supposed to go from 0 -> 2^32, which in real time would represent (0 -> 2^32 * TRC_HWTC_DIVISOR) ticks. */
/* First we see how long time has passed since the last timestamp call, and we also add the ticks that was lost when we scaled down the last time. */
diff = ( hwtc_count - last_hwtc_count ) + last_hwtc_rest;
/* Scale down the diff */
diff_scaled = diff / ( TRC_HWTC_DIVISOR );
/* Find out how many ticks were lost when scaling down, so we can add them the next time */
last_hwtc_rest = diff % ( TRC_HWTC_DIVISOR );
/* We increase the scaled timestamp by the scaled amount */
last_timestamp += diff_scaled;
#endif /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
/* Is anyone interested in the results? */
if( pTimestamp )
{
*pTimestamp = last_timestamp;
}
/* Store the previous value */
last_hwtc_count = hwtc_count;
}
#if defined( TRC_CFG_ENABLE_STACK_MONITOR ) && ( TRC_CFG_ENABLE_STACK_MONITOR == 1 ) && ( TRC_CFG_SCHEDULING_ONLY == 0 )
void prvAddTaskToStackMonitor( void * task )
{
int i;
int foundEmptySlot = 0;
/* find an empty slot */
for( i = 0; i < TRC_CFG_STACK_MONITOR_MAX_TASKS; i++ )
{
if( tasksInStackMonitor[ i ].tcb == 0 )
{
tasksInStackMonitor[ i ].tcb = task;
tasksInStackMonitor[ i ].uiPreviousLowMark = 0xFFFFFFFF;
foundEmptySlot = 1;
break;
}
}
if( foundEmptySlot == 0 )
{
tasksNotIncluded++;
}
}
void prvRemoveTaskFromStackMonitor( void * task )
{
int i;
for( i = 0; i < TRC_CFG_STACK_MONITOR_MAX_TASKS; i++ )
{
if( tasksInStackMonitor[ i ].tcb == task )
{
tasksInStackMonitor[ i ].tcb = 0;
tasksInStackMonitor[ i ].uiPreviousLowMark = 0;
}
}
}
void prvReportStackUsage()
{
static int i = 0; /* Static index used to loop over the monitored tasks */
int count = 0; /* The number of generated reports */
int initial = i; /* Used to make sure we break if we are back at the inital value */
do
{
/* Check the current spot */
if( tasksInStackMonitor[ i ].tcb != 0 )
{
/* Get the amount of unused stack */
TraceUnsignedBaseType_t unusedStackSpace;
xTraceKernelPortGetUnusedStack( tasksInStackMonitor[ i ].tcb, &unusedStackSpace );
/* Store for later use */
if( tasksInStackMonitor[ i ].uiPreviousLowMark > unusedStackSpace )
{
tasksInStackMonitor[ i ].uiPreviousLowMark = unusedStackSpace;
}
prvTraceStoreKernelCallWithParam( TRACE_UNUSED_STACK, TRACE_CLASS_TASK, TRACE_GET_TASK_NUMBER( tasksInStackMonitor[ i ].tcb ), tasksInStackMonitor[ i ].uiPreviousLowMark );
count++;
}
i = ( i + 1 ) % TRC_CFG_STACK_MONITOR_MAX_TASKS; /* Move i beyond this task */
} while( count < TRC_CFG_STACK_MONITOR_MAX_REPORTS && i != initial );
}
#endif /* defined(TRC_CFG_ENABLE_STACK_MONITOR) && (TRC_CFG_ENABLE_STACK_MONITOR == 1) && (TRC_CFG_SCHEDULING_ONLY == 0) */
#endif /* (TRC_USE_TRACEALYZER_RECORDER == 1) */
#endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)*/