/* * 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 #if ( TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT ) #if ( TRC_USE_TRACEALYZER_RECORDER == 1 ) #include #include #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( ×tamp ); /*************************************************************************** * 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)*/