| /******************************************************************************* | |
| * FreeRTOS+Trace v2.3.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 trcPort.c and trcPort.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. | |
| * | |
| * FreeRTOS+Trace is available as Free Edition and in two premium editions. | |
| * You may use the premium features during 30 days for evaluation. | |
| * Download FreeRTOS+Trace at http://www.percepio.com/products/downloads/ | |
| * | |
| * Copyright Percepio AB, 2012. | |
| * www.percepio.com | |
| ******************************************************************************/ | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include "trcUser.h" | |
| #include "task.h" | |
| #include "semphr.h" | |
| #if (configUSE_TRACE_FACILITY == 1) | |
| extern uint8_t inExcludedTask; | |
| extern uint8_t nISRactive; | |
| extern uint8_t 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 unsigned char ucQueueGetQueueType(void*); | |
| extern unsigned char ucQueueGetQueueNumber(void*); | |
| extern char* traceErrorMessage; | |
| static void vTraceMonitorTask(void); | |
| static void prvTraceExcludeOrIncludeKernelServiceFromTrace(traceKernelService, uint8_t); | |
| /******************************************************************************* | |
| * vTraceMonitorTask | |
| * | |
| * A task which periodically reports the recorder status to the console. | |
| * This is included depending on USE_TRACE_PROGRESS_MONITOR_TASK. | |
| ******************************************************************************/ | |
| static void vTraceMonitorTask(void) | |
| { | |
| portTickType xNextWakeTime; | |
| char localsprintbuffer[90]; | |
| char* err = NULL; | |
| char* lastErr = NULL; | |
| #define STATE_INIT 0 | |
| #define STATE_STARTED 1 | |
| #define STATE_STOPPED 2 | |
| int state = STATE_INIT; | |
| vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Monitor task started...\n\r"); | |
| /* Initialise xNextWakeTime - this only needs to be done once. */ | |
| xNextWakeTime = xTaskGetTickCount(); | |
| for (;;) | |
| { | |
| lastErr = err; | |
| err = xTraceGetLastError(); | |
| if (err != lastErr) | |
| { | |
| sprintf(localsprintbuffer, "\n\r[FreeRTOS+Trace] Error: %s\n\r", err); | |
| vTraceConsoleMessage(localsprintbuffer); | |
| } | |
| if (state == STATE_STOPPED || state == STATE_INIT) | |
| { | |
| if (RecorderDataPtr->recorderActive == 1) | |
| { | |
| state = STATE_STARTED; | |
| vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Recorder started.\n\r"); | |
| } | |
| else | |
| { | |
| if (state == STATE_INIT) | |
| { | |
| vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Recorder not started.\n\r"); | |
| state = STATE_STOPPED; | |
| } | |
| } | |
| } | |
| if (state == STATE_STARTED) | |
| { | |
| if (RecorderDataPtr->frequency > 0) | |
| { | |
| sprintf(localsprintbuffer, | |
| "\n\r[FreeRTOS+Trace] Event count: %d, Duration: %d ms. [%d ticks]\n\r", | |
| (int)RecorderDataPtr->numEvents, | |
| (int)(RecorderDataPtr->absTimeLastEventSecond*1000 + (RecorderDataPtr->absTimeLastEvent*1000)/ RecorderDataPtr->frequency), (int)xTaskGetTickCount()); | |
| vTraceConsoleMessage(localsprintbuffer); | |
| } | |
| if (RecorderDataPtr->recorderActive == 0) | |
| { | |
| state = STATE_STOPPED; | |
| vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Recorder stopped.\n\r"); | |
| vTracePortEnd(); | |
| } | |
| } | |
| /* Place this task in the blocked state until it is time to run again. */ | |
| vTaskDelayUntil( &xNextWakeTime, TRACE_PROGRESS_MONITOR_TASK_PERIOD); | |
| } | |
| } | |
| /******************************************************************************* | |
| * vTraceClear | |
| * | |
| * Resets the recorder. Only necessary if a restart is desired - this is not | |
| * needed in the startup initialization. | |
| ******************************************************************************/ | |
| void vTraceClear(void) | |
| { | |
| trcCRITICAL_SECTION_BEGIN(); | |
| RecorderDataPtr->absTimeLastEvent = 0; | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| RecorderDataPtr->numEvents = 0; | |
| RecorderDataPtr->bufferIsFull = 0; | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * vTraceStartStatusMonitor | |
| * | |
| * This starts a task to monitor the state of½ the recorder. | |
| * This task periodically prints a line to the console window, which shows the | |
| * number of events recorded and the latest timestamp. This task | |
| * calls vTracePortEnd when the recorder has been stopped, where custom | |
| * actions can be added, e.g., to store the trace to a file | |
| * if a file system is available on the device. | |
| ******************************************************************************/ | |
| void vTraceStartStatusMonitor(void) | |
| { | |
| vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Starting Trace Status Monitor...\n\r"); | |
| (void)xTaskCreate( (pdTASK_CODE)vTraceMonitorTask, (const signed char*)"TraceMon", TRACE_PROGRESS_MONITOR_TASK_STACKSIZE, NULL, TRACE_PROGRESS_MONITOR_TASK_PRIORITY, NULL ); | |
| } | |
| /******************************************************************************* | |
| * 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 in | |
| * FreeRTOS+Trace v2.2.2 or later. | |
| ******************************************************************************/ | |
| uint32_t uiTraceStart(void) | |
| { | |
| if (traceErrorMessage == NULL) | |
| { | |
| trcCRITICAL_SECTION_BEGIN(); | |
| RecorderDataPtr->recorderActive = 1; | |
| vTraceStoreTaskswitch(); /* 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 is that is desired. | |
| ******************************************************************************/ | |
| void vTraceStop(void) | |
| { | |
| RecorderDataPtr->recorderActive = 0; | |
| } | |
| /******************************************************************************* | |
| * xTraceGetLastError | |
| * | |
| * Gives the last error message, if any. NULL if no error message is stored. | |
| * The message is cleared on read. | |
| * Any error message is also presented when opening a trace file in | |
| * FreeRTOS+Trace v2.2.2 or later. | |
| ******************************************************************************/ | |
| char* xTraceGetLastError(void) | |
| { | |
| return traceErrorMessage; | |
| } | |
| /******************************************************************************* | |
| * 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); | |
| } | |
| /******************************************************************************* | |
| * prvTraceExcludeOrIncludeKernelServiceFromTrace | |
| * | |
| * Includes or excludes all events that is related to the kernel service. | |
| ******************************************************************************/ | |
| static void prvTraceExcludeOrIncludeKernelServiceFromTrace(traceKernelService kernelService, uint8_t flag) | |
| { | |
| switch(kernelService) | |
| { | |
| case TRACE_KERNEL_SERVICE_TASK_CREATE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_TASK); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_TASK); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_TASK); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_TASK); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_TASK_DELETE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_TASK); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_TASK); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_TASK_DELAY: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY_UNTIL); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY_UNTIL); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_PRIORITY_SET: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_PRIORITY_SET); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_PRIORITY_SET); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_TASK_SUSPEND: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_SUSPEND); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_SUSPEND); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_TASK_RESUME: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME_FROM_ISR); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME_FROM_ISR); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_QUEUE_CREATE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_QUEUE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_QUEUE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_QUEUE_DELETE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_QUEUE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_QUEUE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_QUEUE_SEND: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_QUEUE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_QUEUE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_QUEUE_RECEIVE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_QUEUE_PEEK: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_QUEUE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_MUTEX); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_QUEUE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_MUTEX); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_MUTEX_CREATE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_MUTEX); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_MUTEX); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_MUTEX_DELETE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_MUTEX); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_MUTEX); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_MUTEX_GIVE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_MUTEX); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_MUTEX); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_MUTEX_TAKE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_SEMAPHORE_CREATE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_SEMAPHORE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_SEMAPHORE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_SEMAPHORE_DELETE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_SEMAPHORE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_SEMAPHORE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_SEMAPHORE_GIVE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| } | |
| break; | |
| case TRACE_KERNEL_SERVICE_SEMAPHORE_TAKE: | |
| if (flag) | |
| { | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| } | |
| else | |
| { | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE); | |
| } | |
| break; | |
| } | |
| } | |
| /****************************************************************************** | |
| * vTraceExclude______FromTrace | |
| * | |
| * Excludes a task or object from the trace. | |
| * This can be useful if some irrelevant task is very frequent and is "eating | |
| * up the buffer". This should be called after the task has been created, but | |
| * before starting the FreeRTOS scheduler. | |
| *****************************************************************************/ | |
| void vTraceExcludeQueueFromTrace(void* handle) | |
| { | |
| SET_QUEUE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle)); | |
| } | |
| void vTraceExcludeSemaphoreFromTrace(void* handle) | |
| { | |
| SET_SEMAPHORE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle)); | |
| } | |
| void vTraceExcludeMutexFromTrace(void* handle) | |
| { | |
| SET_MUTEX_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle)); | |
| } | |
| void vTraceExcludeTaskFromTrace(void* handle) | |
| { | |
| SET_TASK_FLAG_ISEXCLUDED(uxTaskGetTaskNumber(handle)); | |
| } | |
| void vTraceExcludeKernelServiceFromTrace(traceKernelService kernelService) | |
| { | |
| prvTraceExcludeOrIncludeKernelServiceFromTrace(kernelService, 1); | |
| } | |
| /****************************************************************************** | |
| * vTraceInclude______InTrace | |
| * | |
| * Includes a task, object or kernel service in the trace. This is only | |
| * necessary if the task or object has been previously exluded. | |
| *****************************************************************************/ | |
| void vTraceIncludeQueueInTrace(void* handle) | |
| { | |
| CLEAR_QUEUE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle)); | |
| } | |
| void vTraceIncludeSemaphoreInTrace(void* handle) | |
| { | |
| CLEAR_SEMAPHORE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle)); | |
| } | |
| void vTraceIncludeMutexInTrace(void* handle) | |
| { | |
| CLEAR_MUTEX_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle)); | |
| } | |
| void vTraceIncludeTaskInTrace(void* handle) | |
| { | |
| CLEAR_TASK_FLAG_ISEXCLUDED(uxTaskGetTaskNumber(handle)); | |
| } | |
| void vTraceIncludeKernelServiceInTrace(traceKernelService kernelService) | |
| { | |
| prvTraceExcludeOrIncludeKernelServiceFromTrace(kernelService, 0); | |
| } | |
| /******************************************************************************* | |
| * vTraceSetQueueName | |
| * | |
| * Assigns a name to a FreeRTOS Queue, Semaphore or Mutex. This function should | |
| * be called right after creation of the queue/mutex/semaphore. If not using | |
| * this function, the queues/mutexes/semaphores will be presented by their | |
| * numeric handle only. | |
| * | |
| * Example: | |
| * actuatorQ = xQueueCreate(3, sizeof(QueueMessage)); | |
| * vTraceSetQueueName(actuatorQ, "ActuatorQueue"); | |
| ******************************************************************************/ | |
| void vTraceSetQueueName(void* queue, const char* name) | |
| { | |
| int t = ucQueueGetQueueType(queue); | |
| vTraceSetObjectName(TraceObjectClassTable[t], | |
| (objectHandleType)ucQueueGetQueueNumber(queue), name); | |
| } | |
| /****************************************************************************** | |
| * 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 next | |
| * kernel call of the task, e.g., a taskDelay or a queue receive. This function | |
| * should be called right before the api function call considered to be the end | |
| * of the current task instance, i.e., the Instance Finish Event. | |
| *****************************************************************************/ | |
| void vTraceTaskInstanceIsFinished() | |
| { | |
| if (handle_of_last_logged_task) | |
| { | |
| SET_TASK_FLAG_MARKIFE(handle_of_last_logged_task); | |
| } | |
| } | |
| /******************************************************************************* | |
| * vvTraceTaskSkipDefaultInstanceFinishedEvents | |
| * | |
| * This is useful if there are implicit Instance Finish Events, such as | |
| * vTaskDelayUntil or xQueueReceive, in a task where an explicit Instance Finish | |
| * Event has been defined. This function tells the recorder that only the | |
| * explicitly defined functions, using vTraceTaskInstanceIsFinished, should be | |
| * treated as Instance Finish Events for this task. The implicit Instance Finish | |
| * Events are thus disregarded for the calling task. | |
| ******************************************************************************/ | |
| void vTraceTaskSkipDefaultInstanceFinishedEvents() | |
| { | |
| if (handle_of_last_logged_task) | |
| { | |
| PROPERTY_TASK_IFE_SERVICECODE(handle_of_last_logged_task) = | |
| RESERVED_DUMMY_CODE; | |
| } | |
| } | |
| /******************************************************************************* | |
| * 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(), which | |
| * typically is mapped to portENTER_CRITICAL(), which uses the macro | |
| * portDISABLE_INTERRUPTS. However, in some ports of FreeRTOS and depending on | |
| * FreeRTOS configuration, 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) | |
| { | |
| vTraceSetObjectName(TRACE_CLASS_ISR, handle, name); | |
| vTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority); | |
| } | |
| /******************************************************************************* | |
| * 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(); | |
| * } | |
| * | |
| * NOTE: You need to make sure that any traced interrupts actually are | |
| * disabled by trcCRITICAL_SECTION_BEGIN(), i.e., taskENTER_CRITICAL() which | |
| * uses portDISABLE_INTERRUPTS(). | |
| * In some ports of FreeRTOS, this does not disable high-priority interrupts, | |
| * i.e., with priorities above configMAX_SYSCALL_INTERRUPT_PRIORITY. | |
| * If an invalid call to vTraceStoreISRBegin is detected (i.e., that preempted | |
| * a critical section of the recorder) this will generate a recorder error | |
| * using vTraceError. | |
| ******************************************************************************/ | |
| void vTraceStoreISRBegin(objectHandleType handle) | |
| { | |
| uint16_t dts4; | |
| TSEvent* ts = NULL; | |
| if (recorder_busy) | |
| { | |
| vTraceError("Illegal call to vTraceStoreISRBegin/End"); | |
| return; | |
| } | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| trcCRITICAL_SECTION_BEGIN(); | |
| dts4 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| if (nISRactive < MAX_ISR_NESTING) | |
| { | |
| isrstack[nISRactive] = handle; | |
| nISRactive++; | |
| ts = (TSEvent*)xTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| ts->type = TS_ISR_BEGIN; | |
| ts->dts = dts4; | |
| ts->objHandle = handle; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| else | |
| { | |
| /* This should not occur unless something is very wrong */ | |
| vTraceError("Too many nested interrupts!"); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| } | |
| #if (SELECTED_PORT == PORT_ARM_CortexM) | |
| int tailchain_irq_pending(void); | |
| /******************************************************************************* | |
| * tailchain_irq_pending | |
| * | |
| * For Cortex-M chips only. Returns 1 if an interrupt is pending, by checking | |
| * the 8 NVIC IRQ pend registers at 0xE000E200 to 0xE000E21C. Returns 0 if no | |
| * interrupt is pending. This is used to predict tailchaining of ISRs. | |
| ******************************************************************************/ | |
| int tailchain_irq_pending(void) | |
| { | |
| uint32_t* pend_reg = ((uint32_t*)0xE000E200); | |
| int i; | |
| for (i=0; i<8; i++) | |
| { | |
| if (pend_reg[i] != 0) | |
| { | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * 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(); | |
| * } | |
| * | |
| * NOTE: You need to make sure that any traced interrupts actually are | |
| * disabled by trcCRITICAL_SECTION_BEGIN(), i.e., taskENTER_CRITICAL() which | |
| * uses portDISABLE_INTERRUPTS(). | |
| * In some ports of FreeRTOS, this does not disable high-priority interrupts, | |
| * i.e., with priorities above configMAX_SYSCALL_INTERRUPT_PRIORITY. | |
| * If an invalid call to vTraceStoreISREnd is detected (i.e., that preempted | |
| * a critical section of the recorder) this will generate a recorder error | |
| * using vTraceError. | |
| ******************************************************************************/ | |
| void vTraceStoreISREnd(void) | |
| { | |
| TSEvent* ts; | |
| uint16_t dts5; | |
| if (recorder_busy) | |
| { | |
| vTraceError("Illegal call to vTraceStoreISRBegin/End"); | |
| return; | |
| } | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| #if (SELECTED_PORT == PORT_ARM_CortexM) | |
| if (tailchain_irq_pending() > 0) | |
| { | |
| nISRactive--; /* If an IRQ strikes exactly here, the resulting | |
| ISR tailchaining is not detected. The trace instead shows a very | |
| short fragment of the earlier preempted task/ISR, and then the new | |
| ISR begins. */ | |
| return; | |
| } | |
| #endif | |
| trcCRITICAL_SECTION_BEGIN(); | |
| dts5 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| ts = (TSEvent*)xTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| if (nISRactive > 1) | |
| { | |
| /* return to another isr */ | |
| ts->type = TS_ISR_RESUME; | |
| ts->objHandle = isrstack[nISRactive]; | |
| } | |
| else | |
| { | |
| /* return to task */ | |
| ts->type = TS_TASK_RESUME; | |
| ts->objHandle = handle_of_last_logged_task; | |
| } | |
| ts->dts = dts5; | |
| nISRactive--; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| } | |
| #else | |
| /* ISR tracing is turned off */ | |
| void vTraceIncreaseISRActive(void) | |
| { | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| nISRactive++; | |
| } | |
| void vTraceDecreaseISRActive(objectHandleType handle) | |
| { | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| nISRactive--; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * User Event functions | |
| ******************************************************************************/ | |
| #if (INCLUDE_USER_EVENTS == 1) | |
| /****************************************************************************** | |
| * 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) | |
| { | |
| UserEvent* ue; | |
| uint8_t dts1; | |
| if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive ) && handle_of_last_logged_task) | |
| { | |
| trcCRITICAL_SECTION_BEGIN(); | |
| 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(); | |
| } | |
| } | |
| /*** Locally used in vTracePrintF ***/ | |
| /* one word (32 bit) is required for the USER_EVENT entry, 8 words | |
| (8*32 bit = 32 byte) available for argument data */ | |
| #define MAX_ARG_SIZE (4+32) | |
| static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value); | |
| static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value); | |
| static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value); | |
| #if (INCLUDE_FLOAT_SUPPORT) | |
| static uint8_t writeFloat(void * buffer, uint8_t i, float value); | |
| static uint8_t writeDouble(void * buffer, uint8_t i, double value); | |
| #endif | |
| /*** Locally used in vTracePrintF ***/ | |
| static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value) | |
| { | |
| 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) | |
| { | |
| /* 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) | |
| { | |
| /* 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) | |
| { | |
| /* 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) | |
| { | |
| uint32_t * dest = buffer; | |
| 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[i/4+0] = src[0]; | |
| dest[i/4+1] = src[1]; | |
| return i + 8; | |
| } | |
| #endif | |
| /****************************************************************************** | |
| * 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, ...) | |
| { | |
| UserEvent* ue1; | |
| va_list vl; | |
| uint8_t argCounter = 0; | |
| uint8_t i = 0; | |
| uint8_t nofEventEntries = 0; | |
| uint16_t formatStrIndex = 0; | |
| /************************************************************************** | |
| * 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) | |
| ***************************************************************************/ | |
| uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4]; | |
| if ((inExcludedTask == 0) && | |
| (nISRactive == 0) && | |
| (RecorderDataPtr->recorderActive == 1) && | |
| (handle_of_last_logged_task > 0)) | |
| { | |
| /* 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 */ | |
| i = 4; | |
| formatStrIndex = 0; | |
| va_start(vl, formatStr); /* Begin reading the arguments list */ | |
| while (formatStr[formatStrIndex] != '\0') | |
| { | |
| if (formatStr[formatStrIndex] == '%') | |
| { | |
| argCounter++; | |
| if (argCounter > 15) | |
| { | |
| vTraceError("vTracePrintF - Too many arguments, max 15 allowed!"); | |
| va_end(vl); | |
| formatStr = "[vTracePrintF error] Too many arguments, max 15 allowed!"; | |
| i = 4; | |
| break; | |
| } | |
| /******************************************************************************* | |
| * 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 followes 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 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++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| case 'd': i = writeInt32((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint32_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 'u': i = writeInt32((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint32_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 's': i = writeInt16((uint8_t*)tempDataBuffer, | |
| 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((uint8_t*)tempDataBuffer, | |
| 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((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint32_t)va_arg(vl, double)); | |
| #endif | |
| case 'l': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| #if (INCLUDE_FLOAT_SUPPORT) | |
| case 'f': i = writeDouble((uint8_t*)tempDataBuffer, | |
| 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((uint8_t*)tempDataBuffer, /* In this case, the value will not be shown anyway */ | |
| i, | |
| (uint32_t)va_arg(vl, double)); | |
| i = writeInt32((uint8_t*)tempDataBuffer, /* Do it twice, to write in total 8 bytes */ | |
| i, | |
| (uint32_t)va_arg(vl, double)); | |
| #endif | |
| } | |
| break; | |
| case 'h': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| case 'd': i = writeInt16((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint16_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 'u': i = writeInt16((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint16_t)va_arg(vl, uint32_t)); | |
| break; | |
| } | |
| break; | |
| case 'b': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| case 'd': i = writeInt8((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint8_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 'u': i = writeInt8((uint8_t*)tempDataBuffer, | |
| i, | |
| (uint8_t)va_arg(vl, uint32_t)); | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| formatStrIndex++; | |
| if (i == 255) | |
| { | |
| va_end(vl); | |
| //vTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!"); | |
| formatStr = "[vTracePrintF error] Too large arguments, max 32 byte allowed!"; | |
| i = 4; | |
| break; | |
| } | |
| } | |
| va_end(vl); | |
| /* Store the format string, with a reference to the channel symbol */ | |
| ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| ue1->dts = (uint8_t)prvTraceGetDTS(0xFF); | |
| if (! RecorderDataPtr->recorderActive) | |
| { | |
| /* Abort, since an XTS event (created by prvTraceGetDTS) filled the | |
| buffer, and the recorder stopped since not circular buffer. */ | |
| trcCRITICAL_SECTION_END(); | |
| return; | |
| } | |
| nofEventEntries = (i+3)/4; | |
| /* 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 + nofEventEntries > RecorderDataPtr->maxEvents) | |
| { | |
| #if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER) | |
| (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4], | |
| 0, | |
| (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4); | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| RecorderDataPtr->bufferIsFull = 1; | |
| #else | |
| /* Abort and stop recorder, since the event data will not fit in the | |
| buffer and not circular buffer in this case... */ | |
| trcCRITICAL_SECTION_END(); | |
| vTraceStop(); | |
| return; | |
| #endif | |
| } | |
| #if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER) | |
| /* 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).*/ | |
| prvCheckDataToBeOverwrittenForMultiEntryUserEvents(nofEventEntries); | |
| #endif | |
| /* Copy the local buffer to the main buffer */ | |
| (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4], | |
| tempDataBuffer, | |
| i); | |
| /* 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 + nofEventEntries - 1; | |
| /* Update the main buffer event index (already checked that it fits in | |
| the buffer, so no need to check for wrapping)*/ | |
| RecorderDataPtr->nextFreeIndex += nofEventEntries; | |
| RecorderDataPtr->numEvents += nofEventEntries; | |
| if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE) | |
| { | |
| #if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER) | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| RecorderDataPtr->bufferIsFull = 1; | |
| #else | |
| vTraceStop(); | |
| #endif | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| } | |
| /******************************************************************************* | |
| * 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) | |
| { | |
| return prvTraceOpenSymbol(label, 0); | |
| } | |
| #endif | |
| #endif | |