| /******************************************************************************* | |
| * Tracealyzer v2.6.0 Recorder Library | |
| * Percepio AB, www.percepio.com | |
| * | |
| * trcUser.c | |
| * | |
| * The public API of the trace recorder library. | |
| * | |
| * Terms of Use | |
| * This software is copyright Percepio AB. The recorder library is free for | |
| * use together with Percepio products. You may distribute the recorder library | |
| * in its original form, including modifications in trcHardwarePort.c/.h | |
| * given that these modification are clearly marked as your own modifications | |
| * and documented in the initial comment section of these source files. | |
| * This software is the intellectual property of Percepio AB and may not be | |
| * sold or in other ways commercially redistributed without explicit written | |
| * permission by Percepio AB. | |
| * | |
| * Disclaimer | |
| * The trace tool and recorder library is being delivered to you AS IS and | |
| * Percepio AB makes no warranty as to its use or performance. Percepio AB does | |
| * not and cannot warrant the performance or results you may obtain by using the | |
| * software or documentation. Percepio AB make no warranties, express or | |
| * implied, as to noninfringement of third party rights, merchantability, or | |
| * fitness for any particular purpose. In no event will Percepio AB, its | |
| * technology partners, or distributors be liable to you for any consequential, | |
| * incidental or special damages, including any lost profits or lost savings, | |
| * even if a representative of Percepio AB has been advised of the possibility | |
| * of such damages, or for any claim by any third party. Some jurisdictions do | |
| * not allow the exclusion or limitation of incidental, consequential or special | |
| * damages, or the exclusion of implied warranties or limitations on how long an | |
| * implied warranty may last, so the above limitations may not apply to you. | |
| * | |
| * Copyright Percepio AB, 2013. | |
| * www.percepio.com | |
| ******************************************************************************/ | |
| #include "FreeRTOS.h" | |
| #include "task.h" | |
| #include "trcUser.h" | |
| #if (USE_TRACEALYZER_RECORDER == 1) | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include <stdint.h> | |
| TRACE_STOP_HOOK vTraceStopHookPtr = (TRACE_STOP_HOOK)0; | |
| extern uint8_t inExcludedTask; | |
| extern uint8_t nISRactive; | |
| extern objectHandleType handle_of_last_logged_task; | |
| extern uint32_t dts_min; | |
| extern uint32_t hwtc_count_max_after_tick; | |
| extern uint32_t hwtc_count_sum_after_tick; | |
| extern uint32_t hwtc_count_sum_after_tick_counter; | |
| extern char* traceErrorMessage; | |
| /*** private functions *******************************************************/ | |
| void vTracePrintF_Helper(traceLabel eventLabel, const char* formatStr, va_list vl); | |
| #if (USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| void vTraceChannelPrintF_Helper(UserEventChannel channelPair, va_list vl); | |
| static void prvTraceUserEventHelper1(UserEventChannel channel, traceLabel eventLabel, traceLabel formatLabel, va_list vl); | |
| static void prvTraceUserEventHelper2(UserEventChannel channel, uint32_t* data, uint32_t noOfSlots); | |
| #endif | |
| /*****************************************************************************/ | |
| /******************************************************************************* | |
| * vTraceInitTraceData | |
| * | |
| * Allocates, if necessary, and initializes the recorder data structure, based | |
| * on the constants in trcConfig.h. | |
| ******************************************************************************/ | |
| void vTraceInitTraceData(void) | |
| { | |
| prvTraceInitTraceData(); | |
| } | |
| /******************************************************************************* | |
| * vTraceSetRecorderData | |
| * | |
| * If custom allocation is used, this function must be called so the recorder | |
| * library knows where to save the trace data. | |
| ******************************************************************************/ | |
| #if TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_CUSTOM | |
| void vTraceSetRecorderData(void* pRecorderData) | |
| { | |
| TRACE_ASSERT(pRecorderData != NULL, "vTraceSetTraceData, pRecorderData == NULL", ); | |
| RecorderDataPtr = pRecorderData; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * vTraceClear | |
| * | |
| * Resets the recorder. Only necessary if a restart is desired - this is not | |
| * needed in the startup initialization. | |
| ******************************************************************************/ | |
| void vTraceClear(void) | |
| { | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| RecorderDataPtr->absTimeLastEvent = 0; | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| RecorderDataPtr->numEvents = 0; | |
| RecorderDataPtr->bufferIsFull = 0; | |
| traceErrorMessage = NULL; | |
| RecorderDataPtr->internalErrorOccured = 0; | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * uiTraceStart | |
| * | |
| * Starts the recorder. The recorder will not be started if an error has been | |
| * indicated using vTraceError, e.g. if any of the Nx constants in trcConfig.h | |
| * has a too small value (NTASK, NQUEUE, etc). | |
| * | |
| * Returns 1 if the recorder was started successfully. | |
| * Returns 0 if the recorder start was prevented due to a previous internal | |
| * error. In that case, check vTraceGetLastError to get the error message. | |
| * Any error message is also presented when opening a trace file. | |
| ******************************************************************************/ | |
| uint32_t uiTraceStart(void) | |
| { | |
| objectHandleType handle; | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| handle = 0; | |
| if (RecorderDataPtr == NULL) | |
| { | |
| vTraceError("RecorderDataPtr is NULL. Call vTraceInitTraceData() before starting trace."); | |
| return 0; | |
| } | |
| if (traceErrorMessage == NULL) | |
| { | |
| 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 = xTraceGetObjectHandle(TRACE_CLASS_TASK); | |
| vTraceSetObjectName(TRACE_CLASS_TASK, handle, "(startup)"); | |
| vTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0); | |
| } | |
| vTraceStoreTaskswitch(handle); /* Register the currently running task */ | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| return RecorderDataPtr->recorderActive; | |
| } | |
| /******************************************************************************* | |
| * vTraceStart | |
| * | |
| * Starts the recorder. The recorder will not be started if an error has been | |
| * indicated using vTraceError, e.g. if any of the Nx constants in trcConfig.h | |
| * has a too small value (NTASK, NQUEUE, etc). | |
| * | |
| * This function is obsolete, but has been saved for backwards compatibility. | |
| * We recommend using uiTraceStart instead. | |
| ******************************************************************************/ | |
| void vTraceStart(void) | |
| { | |
| (void)uiTraceStart(); | |
| } | |
| /******************************************************************************* | |
| * vTraceStop | |
| * | |
| * Stops the recorder. The recording can be resumed by calling vTraceStart. | |
| * This does not reset the recorder. Use vTraceClear if that is desired. | |
| ******************************************************************************/ | |
| void vTraceStop(void) | |
| { | |
| RecorderDataPtr->recorderActive = 0; | |
| if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0) | |
| { | |
| (*vTraceStopHookPtr)(); /* Call an application level call back function. */ | |
| } | |
| } | |
| /******************************************************************************* | |
| * xTraceGetLastError | |
| * | |
| * 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. | |
| ******************************************************************************/ | |
| char* xTraceGetLastError(void) | |
| { | |
| return traceErrorMessage; | |
| } | |
| /******************************************************************************* | |
| * vTraceClearError | |
| * | |
| * Removes any previous error message generated by recorder calling vTraceError. | |
| * 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(int resetErrorMessage) | |
| { | |
| ( void ) resetErrorMessage; | |
| traceErrorMessage = NULL; | |
| RecorderDataPtr->internalErrorOccured = 0; | |
| } | |
| /******************************************************************************* | |
| * vTraceGetTraceBuffer | |
| * | |
| * 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* vTraceGetTraceBuffer(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); | |
| } | |
| /****************************************************************************** | |
| * vTraceTaskInstanceIsFinished | |
| * | |
| * This defines an explicit Instance Finish Event for the current task. It tells | |
| * the recorder that the current instance of this task is finished at the | |
| * context-switch. This function should be called right before the API function | |
| * call considered to be the Instance Finish Event. | |
| *****************************************************************************/ | |
| void vTraceTaskInstanceIsFinished() | |
| { | |
| if (handle_of_last_logged_task) | |
| { | |
| TRACE_PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle_of_last_logged_task) = 0; | |
| } | |
| } | |
| /******************************************************************************* | |
| * Interrupt recording functions | |
| ******************************************************************************/ | |
| #if (INCLUDE_ISR_TRACING == 1) | |
| #define MAX_ISR_NESTING 16 | |
| static uint8_t isrstack[MAX_ISR_NESTING]; | |
| /******************************************************************************* | |
| * vTraceSetISRProperties | |
| * | |
| * Registers an Interrupt Service Routine in the recorder library, This must be | |
| * called before using vTraceStoreISRBegin to store ISR events. This is | |
| * typically called in the startup of the system, before the scheduler is | |
| * started. | |
| * | |
| * Example: | |
| * #define ID_ISR_TIMER1 1 // lowest valid ID is 1 | |
| * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt | |
| * ... | |
| * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1); | |
| * ... | |
| * void ISR_handler() | |
| * { | |
| * vTraceStoreISRBegin(ID_OF_ISR_TIMER1); | |
| * ... | |
| * vTraceStoreISREnd(); | |
| * } | |
| * | |
| * NOTE: To safely record ISRs, you need to make sure that all traced | |
| * interrupts actually are disabled by trcCRITICAL_SECTION_BEGIN(). However, | |
| * in some ports this does not disable high priority interrupts! | |
| * If an ISR calls vTraceStoreISRBegin while the recorder is busy, it will | |
| * stop the recording and give an error message. | |
| ******************************************************************************/ | |
| void vTraceSetISRProperties(objectHandleType handle, const char* name, char priority) | |
| { | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceSetISRProperties: Invalid value for handle", ); | |
| TRACE_ASSERT(name != NULL, "vTraceSetISRProperties: name == NULL", ); | |
| TRACE_ASSERT(priority >= 0, "vTraceSetISRProperties: Invalid value for priority", ); | |
| vTraceSetObjectName(TRACE_CLASS_ISR, handle, name); | |
| vTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority); | |
| } | |
| #if (SELECTED_PORT == PORT_ARM_CortexM) | |
| /****************************************************************************** | |
| * (Advanced...) | |
| * | |
| * ISR_TAILCHAINING_THRESHOLD (For Cortex-M devices only) | |
| * | |
| * ARM Cortex-M devices may execute ISRs back-to-back (tail-chained) without | |
| * resuming the previous context in between. Since this is decided in | |
| * hardware, we can only detect this indirectly, in the following manner: | |
| * | |
| * When entering vTraceStoreISRBegin, we check the number of CPU cycles since | |
| * the last return of vTraceStoreISREnd. If less or equal to the constant | |
| * ISR_TAILCHAINING_THRESHOLD it is assumed that the ISRs executed back-to-back | |
| * (tail-chained). In that case, the previously stored RESUME event | |
| * (pointed to by ptrLastISRExitEvent) is then deleted to avoid showing a | |
| * fragment of the previous context in between the ISR events. The delete is | |
| * made by replacing the event code with a XTS16L event, that serves to keep | |
| * the differential timestamp from the earlier event. | |
| * | |
| * The value of ISR_TAILCHAINING_THRESHOLD depends on the interrupt latency of | |
| * the processor, on the compiler and on the compiler settings, but should be | |
| * around 70 cycles. The default value is 66 cycles, which should be correct when | |
| * using GCC with optimizations disabled (-O0) and Cortex-M3/M4. | |
| * | |
| * To measure this value, see MEASURE_ISR_TAILCHAINING_THRESHOLD below. | |
| * | |
| * If this value set too low, tail-chained ISRs will incorrectly be shown | |
| * separated, with a short fragment of the previous task or ISR in between. | |
| * If this value is set too high, you get the opposite effect - separate ISRs | |
| * will appear to execute tail-chained and will appear to have higher execution | |
| * time and response time (maximum ISR_TAILCHAINING_THRESHOLD cycles more). | |
| *****************************************************************************/ | |
| #define ISR_TAILCHAINING_THRESHOLD 66 | |
| uint8_t* ptrLastISRExitEvent = NULL; | |
| uint32_t DWTCycleCountAtLastISRExit = 0; | |
| /****************************************************************************** | |
| * (Advanced...) | |
| * | |
| * MEASURE_ISR_TAILCHAINING_THRESHOLD (For Cortex-M devices only) | |
| * | |
| * Allows for measuring the value of ISR_TAILCHAINING_THRESHOLD (see above). | |
| * | |
| * This is intended to measure the minimum number of clock cycles from the end | |
| * of vTraceStoreISREnd to the beginning of the following vTraceStoreISRBegin. | |
| * For this purpose, we assume a test setup using the SysTick interrupt, which | |
| * is available on most Cortex-M devices and typically used by the RTOS kernel. | |
| * To do the measurement, follow these steps: | |
| * | |
| * 1. Make sure MEASURE_ISR_TAILCHAINING_THRESHOLD is enabled (defined as 1) | |
| * | |
| * 2. Temporarily replace your SysTick handler with the following: | |
| * | |
| * void xPortSysTickHandler( void ) | |
| * { | |
| * vTraceStoreISRBegin(1); | |
| * vTraceStoreISREnd(); | |
| * } | |
| * | |
| * 3. To make sure that the ISRs execute back-to-back, increase the OS tick | |
| * frequency to a very high level so that the OS tick interrupt execute | |
| * continuously with no application tasks in between. A tick frequency of | |
| * 1 MHz (1.000.000) should be sufficient. | |
| * | |
| * 4. Put a breakpoint in the highest priority task and make sure it is not | |
| * reached. This means that the SysTick handler is executing at maximum rate | |
| * and thereby tail-chained, where the interrupt latency is 6 cycles. | |
| * | |
| * 5. Let the system run without breakpoints and inspect the value of | |
| * threshold_low_watermark. This is the minimum total latency observed. | |
| * The hardware latency is 6 clock cycles due to the tail-chaining, so the | |
| * software latency (SL) is then SL = threshold_low_watermark - 6. | |
| * | |
| * The threshold value ISR_TAILCHAINING_THRESHOLD should be SL + 2 * HL, where | |
| * HL is the normal hardware interrupt latency, i.e., the number of CPU | |
| * cycles to enter or exit the exception handler for an exception in task | |
| * context. The HL value is 12-16 depending on core, as shown below. | |
| * | |
| * Values for ISR_TAILCHAINING_THRESHOLD, assuming SL = 42 | |
| * Cortex-M3 and M4 (HL = 12): 66 cycles | |
| * Cortex-M0 (HL = 16): 74 cycles | |
| * Cortex-M0+ (HL = 15): 72 cycles | |
| * | |
| * If the ISR_TAILCHAINING_THRESHOLD value is set too low, some tail-chained | |
| * ISRs be shown separated, with a short fragment of the previous actor. If | |
| * the value is set too high, separate ISRs will appear to execute tail-chained | |
| * and for too long time. | |
| *****************************************************************************/ | |
| #define MEASURE_ISR_TAILCHAINING_THRESHOLD 1 | |
| #if (MEASURE_ISR_TAILCHAINING_THRESHOLD == 1) | |
| volatile uint32_t threshold_low_watermark = 2000000000; | |
| #endif | |
| #endif | |
| /******************************************************************************* | |
| * vTraceStoreISRBegin | |
| * | |
| * Registers the beginning of an Interrupt Service Routine. | |
| * | |
| * Example: | |
| * #define ID_ISR_TIMER1 1 // lowest valid ID is 1 | |
| * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt | |
| * ... | |
| * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1); | |
| * ... | |
| * void ISR_handler() | |
| * { | |
| * vTraceStoreISRBegin(ID_OF_ISR_TIMER1); | |
| * ... | |
| * vTraceStoreISREnd(); | |
| * } | |
| * | |
| ******************************************************************************/ | |
| void vTraceStoreISRBegin(objectHandleType handle) | |
| { | |
| uint16_t dts4; | |
| #if (SELECTED_PORT == PORT_ARM_CortexM) | |
| uint32_t CPUCyclesSinceLastISRExit = DWT_CYCLE_COUNTER - DWTCycleCountAtLastISRExit; | |
| #endif | |
| TSEvent* ts; | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| ts = NULL; | |
| #if (SELECTED_PORT == PORT_ARM_CortexM) | |
| if (DWTCycleCountAtLastISRExit > 0) | |
| { | |
| #if (MEASURE_ISR_TAILCHAINING_THRESHOLD == 1) | |
| /* Allows for verifying the value of ISR_TAILCHAINING_THRESHOLD */ | |
| if (CPUCyclesSinceLastISRExit < threshold_low_watermark) | |
| { | |
| threshold_low_watermark = CPUCyclesSinceLastISRExit; | |
| } | |
| #endif | |
| if (CPUCyclesSinceLastISRExit <= ISR_TAILCHAINING_THRESHOLD) | |
| { | |
| /* This is judged to be a case of ISR tail-chaining since the | |
| number of cycles since the last vTraceStoreISREnd is shorter or equal to | |
| the threshold (ISR_TAILCHAINING_THRESHOLD) */ | |
| if (ptrLastISRExitEvent != NULL) | |
| { | |
| /* Overwrite the last ISR exit event with a "neutral" event that only | |
| accounts for the time passed */ | |
| *ptrLastISRExitEvent = XTS16L; | |
| } | |
| } | |
| } | |
| #endif | |
| if (recorder_busy) | |
| { | |
| vTraceError("Illegal call to vTraceStoreISRBegin, recorder busy!"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid value for handle", ); | |
| dts4 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| if (nISRactive < MAX_ISR_NESTING) | |
| { | |
| uint8_t hnd8 = prvTraceGet8BitHandle(handle); | |
| isrstack[nISRactive] = handle; | |
| nISRactive++; | |
| ts = (TSEvent*)xTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| ts->type = TS_ISR_BEGIN; | |
| ts->dts = dts4; | |
| ts->objHandle = hnd8; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| else | |
| { | |
| /* This should not occur unless something is very wrong */ | |
| vTraceError("Too many nested interrupts!"); | |
| } | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * vTraceStoreISREnd | |
| * | |
| * Registers the end of an Interrupt Service Routine. | |
| * | |
| * Example: | |
| * #define ID_ISR_TIMER1 1 // lowest valid ID is 1 | |
| * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt | |
| * ... | |
| * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1); | |
| * ... | |
| * void ISR_handler() | |
| * { | |
| * vTraceStoreISRBegin(ID_OF_ISR_TIMER1); | |
| * ... | |
| * vTraceStoreISREnd(); | |
| * } | |
| * | |
| ******************************************************************************/ | |
| void vTraceStoreISREnd(void) | |
| { | |
| TSEvent* ts; | |
| uint16_t dts5; | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| if (recorder_busy) | |
| { | |
| vTraceError("Illegal call to vTraceStoreISREnd, recorder busy!"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| dts5 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| uint8_t hnd8, type; | |
| if (nISRactive > 1) | |
| { | |
| /* return to another isr */ | |
| type = TS_ISR_RESUME; | |
| hnd8 = prvTraceGet8BitHandle(isrstack[nISRactive]); | |
| } | |
| else | |
| { | |
| /* return to task */ | |
| type = TS_TASK_RESUME; | |
| hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task); | |
| } | |
| ts = (TSEvent*)xTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| ts->type = type; | |
| ts->objHandle = hnd8; | |
| ts->dts = dts5; | |
| nISRactive--; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| #if (SELECTED_PORT == PORT_ARM_CortexM) | |
| /* Remember the last ISR exit event, as the event needs to be modified in case of a following ISR entry (if tail-chained ISRs) */ | |
| ptrLastISRExitEvent = (uint8_t*)ts; | |
| DWTCycleCountAtLastISRExit = DWT_CYCLE_COUNTER; | |
| #endif | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #else | |
| /* ISR tracing is turned off */ | |
| void vTraceIncreaseISRActive(void) | |
| { | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| nISRactive++; | |
| } | |
| void vTraceDecreaseISRActive(void) | |
| { | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| nISRactive--; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * User Event functions | |
| ******************************************************************************/ | |
| #if (INCLUDE_USER_EVENTS == 1) | |
| #define MAX_ARG_SIZE (4+32) | |
| /*** Locally used in vTracePrintF ***/ | |
| static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "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 i + 1; | |
| } | |
| /*** Locally used in vTracePrintF ***/ | |
| static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "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 i + 2; | |
| } | |
| /*** Locally used in vTracePrintF ***/ | |
| static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "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 i + 4; | |
| } | |
| #if (INCLUDE_FLOAT_SUPPORT) | |
| /*** Locally used in vTracePrintF ***/ | |
| static uint8_t writeFloat(void * buffer, uint8_t i, float value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "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; | |
| } | |
| /*** Locally used in vTracePrintF ***/ | |
| static uint8_t writeDouble(void * buffer, uint8_t i, double value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0); | |
| uint32_t * dest; | |
| uint32_t * src = (void*)&value; | |
| /* 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]); | |
| dest[0] = src[0]; | |
| dest[1] = src[1]; | |
| return i + 8; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceUserEventFormat | |
| * | |
| * Parses the format string and stores the arguments in the buffer. | |
| ******************************************************************************/ | |
| 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] == '%') | |
| { | |
| argCounter++; | |
| if (argCounter > 15) | |
| { | |
| vTraceError("vTracePrintF - Too many arguments, max 15 allowed!"); | |
| return 0; | |
| } | |
| /******************************************************************************* | |
| * These below code writes raw data (primitive datatypes) in the event buffer, | |
| * instead of the normal event structs (where byte 0 is event type). | |
| * These data entries must never be interpreted as real event data, as the type | |
| * field would be misleading since used for payload data. | |
| * | |
| * The correctness of this encoding depends on two mechanisms: | |
| * | |
| * 1. An initial USER_EVENT, which type code tells the number of 32-bit data | |
| * entires that follows. (code - USER_EVENT = number of data entries). | |
| * Note that a data entry corresponds to the slots that normally corresponds to | |
| * one (1) event, i.e., 32 bits. vTracePrintF may encode several pieces of data | |
| * in one data entry, e.g., two 16-bit values or four 8-bit values, one 16-bit | |
| * value followed by two 8-bit values, etc. | |
| * | |
| * 2. A two-phase commit procedure, where the USER_EVENT and data entries are | |
| * written to a local buffer at first, and when all checks are OK then copied to | |
| * the main event buffer using a fast memcpy. The event code is finalized as the | |
| * very last step. Before that step, the event code indicates an unfinished | |
| * event, which causes it to be ignored and stop the loading of the file (since | |
| * an unfinished event is the last event in the trace). | |
| *******************************************************************************/ | |
| formatStrIndex++; | |
| while ((formatStr[formatStrIndex] >= '0' && formatStr[formatStrIndex] <= '9') || formatStr[formatStrIndex] == '#' || formatStr[formatStrIndex] == '.') | |
| formatStrIndex++; | |
| 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': i = writeInt16(buffer, | |
| i, | |
| (uint16_t)xTraceOpenLabel((char*)va_arg(vl, char*))); | |
| break; | |
| #if (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 | |
| case 'l': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| #if (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 | |
| } | |
| 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; | |
| } | |
| 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; | |
| } | |
| break; | |
| } | |
| } | |
| else | |
| break; | |
| } | |
| formatStrIndex++; | |
| if (i == 255) | |
| { | |
| vTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!"); | |
| return 0; | |
| } | |
| } | |
| return (i+3)/4; | |
| } | |
| #if (USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| /******************************************************************************* | |
| * prvTraceClearChannelBuffer | |
| * | |
| * Clears a number of items in the channel buffer, starting from nextSlotToWrite. | |
| ******************************************************************************/ | |
| static void prvTraceClearChannelBuffer(uint32_t count) | |
| { | |
| uint32_t slots; | |
| TRACE_ASSERT(USER_EVENT_BUFFER_SIZE >= count, "prvTraceClearChannelBuffer: USER_EVENT_BUFFER_SIZE is too small to handle this event.", ); | |
| /* Check if we're close to the end of the buffer */ | |
| if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > USER_EVENT_BUFFER_SIZE) | |
| { | |
| slots = 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); | |
| } | |
| /******************************************************************************* | |
| * prvTraceCopyToDataBuffer | |
| * | |
| * Copies a number of items to the data buffer, starting from nextSlotToWrite. | |
| ******************************************************************************/ | |
| static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count) | |
| { | |
| TRACE_ASSERT(data != NULL, "prvTraceCopyToDataBuffer: data == NULL.", ); | |
| TRACE_ASSERT(count <= USER_EVENT_BUFFER_SIZE, "prvTraceCopyToDataBuffer: USER_EVENT_BUFFER_SIZE is too small to handle this event.", ); | |
| uint32_t slots; | |
| /* Check if we're close to the end of the buffer */ | |
| if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > USER_EVENT_BUFFER_SIZE) | |
| { | |
| slots = 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); | |
| } | |
| } | |
| /******************************************************************************* | |
| * prvTraceUserEventHelper1 | |
| * | |
| * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on to the next helper function. | |
| ******************************************************************************/ | |
| static void prvTraceUserEventHelper1(UserEventChannel channel, traceLabel eventLabel, traceLabel 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 += 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); | |
| prvTraceUserEventHelper2(channel, data, noOfSlots); | |
| } | |
| /******************************************************************************* | |
| * prvTraceUserEventHelper2 | |
| * | |
| * This function simply copies the data buffer to the actual user event buffer. | |
| ******************************************************************************/ | |
| static void prvTraceUserEventHelper2(UserEventChannel channel, uint32_t* data, uint32_t noOfSlots) | |
| { | |
| static uint32_t old_timestamp = 0; | |
| uint32_t old_nextSlotToWrite = 0; | |
| TRACE_ASSERT(USER_EVENT_BUFFER_SIZE >= noOfSlots, "vTracePrintF: USER_EVENT_BUFFER_SIZE is too small to handle this event.", ); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| /* Store the timestamp */ | |
| vTracePortGetTimeStamp(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) % 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 | |
| RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (UserEventChannel)0xFF; /* 0xFF indicates that this is not a normal channel id */ | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * xTraceRegisterChannelFormat | |
| * | |
| * Attempts to create a pair of the channel and format string. | |
| * | |
| * Note: This is only available if USE_SEPARATE_USER_EVENT_BUFFER is enabled in | |
| * trcConfig.h | |
| ******************************************************************************/ | |
| UserEventChannel xTraceRegisterChannelFormat(traceLabel channel, traceLabel formatStr) | |
| { | |
| uint8_t i; | |
| UserEventChannel retVal = 0; | |
| TRACE_ASSERT(formatStr != 0, "vTraceRegisterChannelFormat: formatStr == 0", (UserEventChannel)0); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| for (i = 1; i <= CHANNEL_FORMAT_PAIRS; i++) /* Size of the channels buffer is CHANNEL_FORMAT_PAIRS + 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 = (UserEventChannel)i; | |
| break; | |
| } | |
| if (RecorderDataPtr->userEventBuffer.channels[i].name == channel && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == formatStr) | |
| { | |
| /* Found a match */ | |
| retVal = (UserEventChannel)i; | |
| break; | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| return retVal; | |
| } | |
| /****************************************************************************** | |
| * vTraceChannelPrintF | |
| * | |
| * Slightly faster version of vTracePrintF() due to no lookups. | |
| * | |
| * Note: This is only available if USE_SEPARATE_USER_EVENT_BUFFER is enabled in | |
| * trcConfig.h | |
| * | |
| ******************************************************************************/ | |
| void vTraceChannelPrintF(UserEventChannel channelPair, ...) | |
| { | |
| va_list vl; | |
| va_start(vl, channelPair); | |
| vTraceChannelPrintF_Helper(channelPair, vl); | |
| va_end(vl); | |
| } | |
| void vTraceChannelPrintF_Helper(UserEventChannel channelPair, va_list vl) | |
| { | |
| traceLabel channel; | |
| traceLabel formatStr; | |
| TRACE_ASSERT(channelPair != 0, "vTraceChannelPrintF: channelPair == 0", ); | |
| TRACE_ASSERT(channelPair <= CHANNEL_FORMAT_PAIRS, "vTraceChannelPrintF: ", ); | |
| channel = RecorderDataPtr->userEventBuffer.channels[channelPair].name; | |
| formatStr = RecorderDataPtr->userEventBuffer.channels[channelPair].defaultFormat; | |
| prvTraceUserEventHelper1(channelPair, channel, formatStr, vl); | |
| } | |
| /****************************************************************************** | |
| * vTraceChannelUserEvent | |
| * | |
| * Slightly faster version of vTraceUserEvent() due to no lookups. | |
| ******************************************************************************/ | |
| void vTraceChannelUserEvent(UserEventChannel channelPair) | |
| { | |
| uint32_t data[(3 + MAX_ARG_SIZE) / 4]; | |
| TRACE_ASSERT(channelPair != 0, "vTraceChannelPrintF: channelPair == 0", ); | |
| TRACE_ASSERT(channelPair <= CHANNEL_FORMAT_PAIRS, "vTraceChannelPrintF: ", ); | |
| prvTraceUserEventHelper2(channelPair, data, 1); /* Only need one slot for timestamp */ | |
| } | |
| #endif /* USE_SEPARATE_USER_EVENT_BUFFER == 1 */ | |
| /****************************************************************************** | |
| * vTracePrintF | |
| * | |
| * Advanced user events (Professional Edition only) | |
| * | |
| * 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 xTraceOpenLabel. | |
| * Example: | |
| * | |
| * traceLabel adc_uechannel = xTraceOpenLabel("ADC User Events"); | |
| * ... | |
| * vTracePrint(adc_uechannel, | |
| * "ADC channel %d: %lf volts", | |
| * ch, (double)adc_reading/(double)scale); | |
| * | |
| * This can be combined into one line, if desired, but this is slower: | |
| * | |
| * vTracePrint(xTraceOpenLabel("ADC User Events"), | |
| * "ADC channel %d: %lf volts", | |
| * ch, (double)adc_reading/(double)scale); | |
| * | |
| * Calling xTraceOpenLabel 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 vTraceUserEvent - 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 | |
| * | |
| * 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. | |
| ******************************************************************************/ | |
| void vTracePrintF(traceLabel eventLabel, const char* formatStr, ...) | |
| { | |
| va_list vl; | |
| va_start(vl, formatStr); | |
| vTracePrintF_Helper(eventLabel, formatStr, vl); | |
| va_end(vl); | |
| } | |
| void vTracePrintF_Helper(traceLabel eventLabel, const char* formatStr, va_list vl) | |
| { | |
| #if (USE_SEPARATE_USER_EVENT_BUFFER == 0) | |
| uint32_t noOfSlots; | |
| UserEvent* ue1; | |
| uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4]; | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| /************************************************************************** | |
| * The array tempDataBuffer is a local buffer used in a two-phase commit of | |
| * the event data, since a vTracePrintF may span over multiple slots in the | |
| * buffer. | |
| * This buffer can be made larger, of course, but remember the risk for | |
| * stack overflow. Note: This should be a LOCAL buffer, must not be made | |
| * global. That would cause data corruption when two calls to vTracePrintF | |
| * from different tasks overlaps (interrupts are only disabled in a small | |
| * part of this function, otherwise enabled) | |
| ***************************************************************************/ | |
| TRACE_ASSERT(formatStr != NULL, "vTracePrintF: formatStr == NULL", ); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && 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 (TRACE_RECORDER_STORE_MODE == TRACE_STORE_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 (TRACE_RECORDER_STORE_MODE == TRACE_STORE_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 >= EVENT_BUFFER_SIZE) | |
| { | |
| #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_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 (STOP_AFTER_N_EVENTS > -1) | |
| /* Check if we have reached the desired number of events */ | |
| if (RecorderDataPtr->numEvents >= STOP_AFTER_N_EVENTS) | |
| { | |
| vTraceStop(); | |
| } | |
| #endif | |
| } | |
| #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER) | |
| /* Make sure the next entry is cleared correctly */ | |
| prvCheckDataToBeOverwrittenForMultiEntryEvents(1); | |
| #endif | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| #elif (USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| /* Use the separate user event buffer */ | |
| traceLabel formatLabel; | |
| UserEventChannel channel; | |
| if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task) | |
| { | |
| formatLabel = xTraceOpenLabel(formatStr); | |
| channel = xTraceRegisterChannelFormat(eventLabel, formatLabel); | |
| prvTraceUserEventHelper1(channel, eventLabel, formatLabel, vl); | |
| } | |
| #endif | |
| } | |
| /****************************************************************************** | |
| * vTraceUserEvent | |
| * | |
| * Basic user event (Standard and Professional Edition only) | |
| * | |
| * Generates a User Event with a text label. The label is created/looked up | |
| * in the symbol table using xTraceOpenLabel. | |
| ******************************************************************************/ | |
| void vTraceUserEvent(traceLabel eventLabel) | |
| { | |
| #if (USE_SEPARATE_USER_EVENT_BUFFER == 0) | |
| UserEvent* ue; | |
| uint8_t dts1; | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(eventLabel > 0, "vTraceUserEvent: Invalid value for eventLabel", ); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task) | |
| { | |
| dts1 = (uint8_t)prvTraceGetDTS(0xFF); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| ue = (UserEvent*) xTraceNextFreeEventBufferSlot(); | |
| if (ue != NULL) | |
| { | |
| ue->dts = dts1; | |
| ue->type = USER_EVENT; | |
| ue->payload = eventLabel; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| #elif (USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| UserEventChannel channel; | |
| uint32_t noOfSlots = 1; | |
| uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4]; | |
| if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task) | |
| { | |
| channel = xTraceRegisterChannelFormat(0, eventLabel); | |
| if (channel == 0) | |
| { | |
| /* We are dealing with an unknown channel format pair */ | |
| noOfSlots++; /* Also need room for channel and format */ | |
| ((uint16_t*)tempDataBuffer)[2] = 0; | |
| ((uint16_t*)tempDataBuffer)[3] = eventLabel; | |
| } | |
| prvTraceUserEventHelper2(channel, tempDataBuffer, noOfSlots); | |
| } | |
| #endif | |
| } | |
| /******************************************************************************* | |
| * xTraceOpenLabel | |
| * | |
| * Creates user event labels for user event channels or for individual events. | |
| * User events can be used to log application events and data for display in | |
| * the visualization tool. A user event is identified by a label, i.e., a string, | |
| * which is stored in the recorder's symbol table. | |
| * When logging a user event, a numeric handle (reference) to this string is | |
| * used to identify the event. This is obtained by calling | |
| * | |
| * xTraceOpenLabel() | |
| * | |
| * which adds the string to the symbol table (if not already present) | |
| * and returns the corresponding handle. | |
| * | |
| * This can be used in two ways: | |
| * | |
| * 1. The handle is looked up every time, when storing the user event. | |
| * | |
| * Example: | |
| * vTraceUserEvent(xTraceOpenLabel("MyUserEvent")); | |
| * | |
| * 2. The label is registered just once, with the handle stored in an | |
| * application variable - much like using a file handle. | |
| * | |
| * Example: | |
| * myEventHandle = xTraceOpenLabel("MyUserEvent"); | |
| * ... | |
| * vTraceUserEvent(myEventHandle); | |
| * | |
| * The second option is faster since no lookup is required on each event, and | |
| * therefore recommended for user events that are frequently | |
| * executed and/or located in time-critical code. The lookup operation is | |
| * however fairly fast due to the design of the symbol table. | |
| ******************************************************************************/ | |
| traceLabel xTraceOpenLabel(const char* label) | |
| { | |
| TRACE_ASSERT(label != NULL, "xTraceOpenLabel: label == NULL", (traceLabel)0); | |
| return prvTraceOpenSymbol(label, 0); | |
| } | |
| #endif | |
| #endif |