/*******************************************************************************\r
- * FreeRTOS+Trace v2.3.0 Recorder Library\r
+ * Tracealyzer v2.7.0 Recorder Library\r
* Percepio AB, www.percepio.com\r
*\r
* trcUser.c\r
* Terms of Use\r
* This software is copyright Percepio AB. The recorder library is free for\r
* use together with Percepio products. You may distribute the recorder library\r
- * in its original form, including modifications in trcPort.c and trcPort.h\r
+ * in its original form, including modifications in trcHardwarePort.c/.h\r
* given that these modification are clearly marked as your own modifications\r
- * and documented in the initial comment section of these source files. \r
- * This software is the intellectual property of Percepio AB and may not be \r
- * sold or in other ways commercially redistributed without explicit written \r
+ * and documented in the initial comment section of these source files.\r
+ * This software is the intellectual property of Percepio AB and may not be\r
+ * sold or in other ways commercially redistributed without explicit written\r
* permission by Percepio AB.\r
*\r
- * Disclaimer \r
- * The trace tool and recorder library is being delivered to you AS IS and \r
- * Percepio AB makes no warranty as to its use or performance. Percepio AB does \r
- * not and cannot warrant the performance or results you may obtain by using the \r
- * software or documentation. Percepio AB make no warranties, express or \r
- * implied, as to noninfringement of third party rights, merchantability, or \r
- * fitness for any particular purpose. In no event will Percepio AB, its \r
- * technology partners, or distributors be liable to you for any consequential, \r
- * incidental or special damages, including any lost profits or lost savings, \r
- * even if a representative of Percepio AB has been advised of the possibility \r
- * of such damages, or for any claim by any third party. Some jurisdictions do \r
- * not allow the exclusion or limitation of incidental, consequential or special \r
- * damages, or the exclusion of implied warranties or limitations on how long an \r
+ * Disclaimer\r
+ * The trace tool and recorder library is being delivered to you AS IS and\r
+ * Percepio AB makes no warranty as to its use or performance. Percepio AB does\r
+ * not and cannot warrant the performance or results you may obtain by using the\r
+ * software or documentation. Percepio AB make no warranties, express or\r
+ * implied, as to noninfringement of third party rights, merchantability, or\r
+ * fitness for any particular purpose. In no event will Percepio AB, its\r
+ * technology partners, or distributors be liable to you for any consequential,\r
+ * incidental or special damages, including any lost profits or lost savings,\r
+ * even if a representative of Percepio AB has been advised of the possibility\r
+ * of such damages, or for any claim by any third party. Some jurisdictions do\r
+ * not allow the exclusion or limitation of incidental, consequential or special\r
+ * damages, or the exclusion of implied warranties or limitations on how long an\r
* implied warranty may last, so the above limitations may not apply to you.\r
*\r
- * FreeRTOS+Trace is available as Free Edition and in two premium editions.\r
- * You may use the premium features during 30 days for evaluation.\r
- * Download FreeRTOS+Trace at http://www.percepio.com/products/downloads/\r
+ * Tabs are used for indent in this file (1 tab = 4 spaces)\r
*\r
- * Copyright Percepio AB, 2012.\r
+ * Copyright Percepio AB, 2014.\r
* www.percepio.com\r
******************************************************************************/\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+\r
+#include "trcUser.h"\r
+\r
+#if (USE_TRACEALYZER_RECORDER == 1)\r
\r
#include <string.h>\r
#include <stdarg.h>\r
+#include <stdint.h>\r
\r
-#include "trcUser.h"\r
-#include "task.h"\r
-#include "semphr.h"\r
-\r
-#if (configUSE_TRACE_FACILITY == 1)\r
+TRACE_STOP_HOOK vTraceStopHookPtr = (TRACE_STOP_HOOK)0;\r
\r
extern uint8_t inExcludedTask;\r
extern uint8_t nISRactive;\r
-extern uint8_t handle_of_last_logged_task;\r
+extern objectHandleType handle_of_last_logged_task;\r
extern uint32_t dts_min;\r
extern uint32_t hwtc_count_max_after_tick;\r
extern uint32_t hwtc_count_sum_after_tick;\r
extern uint32_t hwtc_count_sum_after_tick_counter;\r
-extern unsigned char ucQueueGetQueueType(void*);\r
-extern unsigned char ucQueueGetQueueNumber(void*);\r
extern char* traceErrorMessage;\r
-static void vTraceMonitorTask(void);\r
-static void prvTraceExcludeOrIncludeKernelServiceFromTrace(traceKernelService, uint8_t);\r
- \r
+\r
+/*** Private functions *******************************************************/\r
+void vTracePrintF_Helper(traceLabel eventLabel, const char* formatStr, va_list vl);\r
+\r
+#if (USE_SEPARATE_USER_EVENT_BUFFER == 1)\r
+void vTraceChannelPrintF_Helper(UserEventChannel channelPair, va_list vl);\r
+static void prvTraceUserEventHelper1(UserEventChannel channel, traceLabel eventLabel, traceLabel formatLabel, va_list vl);\r
+static void prvTraceUserEventHelper2(UserEventChannel channel, uint32_t* data, uint32_t noOfSlots);\r
+#endif\r
+\r
+static void prvTraceTaskInstanceFinish(int8_t direct);\r
+\r
+\r
/*******************************************************************************\r
- * vTraceMonitorTask\r
+ * vTraceInitTraceData\r
*\r
- * A task which periodically reports the recorder status to the console.\r
- * This is included depending on USE_TRACE_PROGRESS_MONITOR_TASK.\r
+ * Allocates, if necessary, and initializes the recorder data structure, based\r
+ * on the constants in trcConfig.h.\r
******************************************************************************/\r
-static void vTraceMonitorTask(void)\r
-{ \r
- portTickType xNextWakeTime;\r
- char localsprintbuffer[90];\r
- char* err = NULL; \r
- char* lastErr = NULL; \r
- #define STATE_INIT 0\r
- #define STATE_STARTED 1\r
- #define STATE_STOPPED 2 \r
- int state = STATE_INIT;\r
- \r
- vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Monitor task started...\n\r");\r
-\r
- /* Initialise xNextWakeTime - this only needs to be done once. */\r
- xNextWakeTime = xTaskGetTickCount();\r
-\r
- for (;;)\r
- {\r
- lastErr = err; \r
- err = xTraceGetLastError();\r
- if (err != lastErr)\r
- {\r
- sprintf(localsprintbuffer, "\n\r[FreeRTOS+Trace] Error: %s\n\r", err);\r
- vTraceConsoleMessage(localsprintbuffer); \r
- }\r
- \r
- if (state == STATE_STOPPED || state == STATE_INIT) \r
- {\r
- if (RecorderDataPtr->recorderActive == 1)\r
- {\r
- state = STATE_STARTED;\r
- vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Recorder started.\n\r"); \r
- }\r
- else\r
- {\r
- if (state == STATE_INIT)\r
- {\r
- \r
- vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Recorder not started.\n\r");\r
- state = STATE_STOPPED;\r
- }\r
- }\r
- }\r
- \r
- if (state == STATE_STARTED)\r
- {\r
- if (RecorderDataPtr->frequency > 0)\r
- {\r
- sprintf(localsprintbuffer, \r
- "\n\r[FreeRTOS+Trace] Event count: %d, Duration: %d ms. [%d ticks]\n\r", \r
- (int)RecorderDataPtr->numEvents, \r
- (int)(RecorderDataPtr->absTimeLastEventSecond*1000 + (RecorderDataPtr->absTimeLastEvent*1000)/ RecorderDataPtr->frequency), (int)xTaskGetTickCount());\r
- vTraceConsoleMessage(localsprintbuffer);\r
- }\r
-\r
- if (RecorderDataPtr->recorderActive == 0)\r
- {\r
- state = STATE_STOPPED;\r
- vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Recorder stopped.\n\r"); \r
- vTracePortEnd();\r
- }\r
- \r
-\r
- }\r
-\r
- /* Place this task in the blocked state until it is time to run again. */\r
- vTaskDelayUntil( &xNextWakeTime, TRACE_PROGRESS_MONITOR_TASK_PERIOD);\r
- \r
- }\r
+void vTraceInitTraceData(void)\r
+{\r
+ prvTraceInitTraceData();\r
}\r
\r
/*******************************************************************************\r
- * vTraceClear\r
+ * vTraceSetRecorderData\r
*\r
- * Resets the recorder. Only necessary if a restart is desired - this is not \r
- * needed in the startup initialization.\r
+ * If custom allocation is used, this function must be called so the recorder\r
+ * library knows where to save the trace data.\r
******************************************************************************/\r
-void vTraceClear(void)\r
+#if TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_CUSTOM\r
+void vTraceSetRecorderData(void* pRecorderData)\r
{\r
- trcCRITICAL_SECTION_BEGIN();\r
- \r
- RecorderDataPtr->absTimeLastEvent = 0;\r
- RecorderDataPtr->nextFreeIndex = 0;\r
- RecorderDataPtr->numEvents = 0;\r
- RecorderDataPtr->bufferIsFull = 0;\r
-\r
- trcCRITICAL_SECTION_END();\r
- \r
+ TRACE_ASSERT(pRecorderData != NULL, "vTraceSetTraceData, pRecorderData == NULL", );\r
+ RecorderDataPtr = pRecorderData;\r
}\r
+#endif\r
\r
/*******************************************************************************\r
- * vTraceStartStatusMonitor\r
- *\r
- * This starts a task to monitor the state of½ the recorder. \r
- * This task periodically prints a line to the console window, which shows the \r
- * number of events recorded and the latest timestamp. This task\r
- * calls vTracePortEnd when the recorder has been stopped, where custom\r
- * actions can be added, e.g., to store the trace to a file\r
- * if a file system is available on the device.\r
+ * vTraceSetStopHook\r
+ *\r
+ * Sets a function to be called when the recorder is stopped.\r
******************************************************************************/\r
-void vTraceStartStatusMonitor(void)\r
-{ \r
- vTraceConsoleMessage("\n\r[FreeRTOS+Trace] Starting Trace Status Monitor...\n\r");\r
- (void)xTaskCreate( (pdTASK_CODE)vTraceMonitorTask, (const signed char*)"TraceMon", TRACE_PROGRESS_MONITOR_TASK_STACKSIZE, NULL, TRACE_PROGRESS_MONITOR_TASK_PRIORITY, NULL );\r
+void vTraceSetStopHook(TRACE_STOP_HOOK stopHookFunction)\r
+{\r
+ vTraceStopHookPtr = stopHookFunction;\r
+}\r
+\r
+/*******************************************************************************\r
+ * vTraceClear\r
+ *\r
+ * Resets the recorder. Only necessary if a restart is desired - this is not\r
+ * needed in the startup initialization.\r
+ ******************************************************************************/\r
+void vTraceClear(void)\r
+{\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
+ trcCRITICAL_SECTION_BEGIN();\r
+\r
+ RecorderDataPtr->absTimeLastEventSecond = 0;\r
+\r
+ RecorderDataPtr->absTimeLastEvent = 0;\r
+ RecorderDataPtr->nextFreeIndex = 0;\r
+ RecorderDataPtr->numEvents = 0;\r
+ RecorderDataPtr->bufferIsFull = 0;\r
+ traceErrorMessage = NULL;\r
+ RecorderDataPtr->internalErrorOccured = 0;\r
+\r
+ memset(RecorderDataPtr->eventData, 0, RecorderDataPtr->maxEvents * 4);\r
+\r
+ trcCRITICAL_SECTION_END();\r
+\r
}\r
\r
/*******************************************************************************\r
* uiTraceStart\r
*\r
* Starts the recorder. The recorder will not be started if an error has been\r
- * indicated using vTraceError, e.g. if any of the Nx constants in trcConfig.h \r
+ * indicated using vTraceError, e.g. if any of the Nx constants in trcConfig.h\r
* has a too small value (NTASK, NQUEUE, etc).\r
- * \r
+ *\r
* Returns 1 if the recorder was started successfully.\r
- * Returns 0 if the recorder start was prevented due to a previous internal \r
+ * Returns 0 if the recorder start was prevented due to a previous internal\r
* error. In that case, check vTraceGetLastError to get the error message.\r
- * Any error message is also presented when opening a trace file in \r
- * FreeRTOS+Trace v2.2.2 or later.\r
+ * Any error message is also presented when opening a trace file.\r
******************************************************************************/\r
\r
uint32_t uiTraceStart(void)\r
-{ \r
- if (traceErrorMessage == NULL)\r
- {\r
- trcCRITICAL_SECTION_BEGIN();\r
- RecorderDataPtr->recorderActive = 1;\r
- vTraceStoreTaskswitch(); /* Register the currently running task */\r
- trcCRITICAL_SECTION_END();\r
- }\r
-\r
- return RecorderDataPtr->recorderActive;\r
+{\r
+ objectHandleType handle;\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
+\r
+ handle = 0;\r
+\r
+ if (RecorderDataPtr == NULL)\r
+ {\r
+ vTraceError("RecorderDataPtr is NULL. Call vTraceInitTraceData() before starting trace.");\r
+ return 0;\r
+ }\r
+\r
+ if (traceErrorMessage == NULL)\r
+ {\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ RecorderDataPtr->recorderActive = 1;\r
+\r
+ handle = TRACE_GET_TASK_NUMBER(TRACE_GET_CURRENT_TASK());\r
+ if (handle == 0)\r
+ {\r
+ /* This occurs if the scheduler is not yet started.\r
+ This creates a dummy "(startup)" task entry internally in the\r
+ recorder */\r
+ handle = xTraceGetObjectHandle(TRACE_CLASS_TASK);\r
+ vTraceSetObjectName(TRACE_CLASS_TASK, handle, "(startup)");\r
+\r
+ vTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0);\r
+ }\r
+\r
+ vTraceStoreTaskswitch(handle); /* Register the currently running task */\r
+ trcCRITICAL_SECTION_END();\r
+ }\r
+\r
+ return RecorderDataPtr->recorderActive;\r
}\r
\r
/*******************************************************************************\r
- * vTraceStart \r
+ * vTraceStart\r
*\r
* Starts the recorder. The recorder will not be started if an error has been\r
- * indicated using vTraceError, e.g. if any of the Nx constants in trcConfig.h \r
+ * indicated using vTraceError, e.g. if any of the Nx constants in trcConfig.h\r
* has a too small value (NTASK, NQUEUE, etc).\r
- * \r
- * This function is obsolete, but has been saved for backwards compatibility. \r
+ *\r
+ * This function is obsolete, but has been saved for backwards compatibility.\r
* We recommend using uiTraceStart instead.\r
******************************************************************************/\r
void vTraceStart(void)\r
-{ \r
- (void)uiTraceStart();\r
+{\r
+ (void)uiTraceStart();\r
}\r
\r
/*******************************************************************************\r
* vTraceStop\r
*\r
* Stops the recorder. The recording can be resumed by calling vTraceStart.\r
- * This does not reset the recorder. Use vTraceClear is that is desired.\r
+ * This does not reset the recorder. Use vTraceClear if that is desired.\r
******************************************************************************/\r
void vTraceStop(void)\r
{\r
- RecorderDataPtr->recorderActive = 0;\r
+ RecorderDataPtr->recorderActive = 0;\r
+\r
+ if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0)\r
+ {\r
+ (*vTraceStopHookPtr)(); /* An application call-back function. */\r
+ }\r
}\r
\r
/*******************************************************************************\r
* xTraceGetLastError\r
*\r
* Gives the last error message, if any. NULL if no error message is stored.\r
- * The message is cleared on read.\r
- * Any error message is also presented when opening a trace file in \r
- * FreeRTOS+Trace v2.2.2 or later.\r
+ * Any error message is also presented when opening a trace file.\r
******************************************************************************/\r
char* xTraceGetLastError(void)\r
-{ \r
- return traceErrorMessage;\r
+{\r
+ return traceErrorMessage;\r
+}\r
+\r
+/*******************************************************************************\r
+* vTraceClearError\r
+*\r
+* Removes any previous error message generated by recorder calling vTraceError.\r
+* By calling this function, it may be possible to start/restart the trace\r
+* despite errors in the recorder, but there is no guarantee that the trace\r
+* recorder will work correctly in that case, depending on the type of error.\r
+******************************************************************************/\r
+void vTraceClearError(int resetErrorMessage)\r
+{\r
+ ( void ) resetErrorMessage;\r
+ traceErrorMessage = NULL;\r
+ RecorderDataPtr->internalErrorOccured = 0;\r
}\r
\r
/*******************************************************************************\r
* vTraceGetTraceBuffer\r
- * \r
- * Returns a pointer to the recorder data structure. Use this together with \r
- * uiTraceGetTraceBufferSize if you wish to implement an own store/upload \r
- * solution, e.g., in case a debugger connection is not available for uploading \r
+ *\r
+ * Returns a pointer to the recorder data structure. Use this together with\r
+ * uiTraceGetTraceBufferSize if you wish to implement an own store/upload\r
+ * solution, e.g., in case a debugger connection is not available for uploading\r
* the data.\r
******************************************************************************/\r
void* vTraceGetTraceBuffer(void)\r
{\r
- return RecorderDataPtr;\r
+ return RecorderDataPtr;\r
}\r
\r
/*******************************************************************************\r
* uiTraceGetTraceBufferSize\r
- * \r
- * Gets the size of the recorder data structure. For use together with \r
- * vTraceGetTraceBuffer if you wish to implement an own store/upload solution, \r
+ *\r
+ * Gets the size of the recorder data structure. For use together with\r
+ * vTraceGetTraceBuffer if you wish to implement an own store/upload solution,\r
* e.g., in case a debugger connection is not available for uploading the data.\r
******************************************************************************/\r
uint32_t uiTraceGetTraceBufferSize(void)\r
{\r
- return sizeof(RecorderDataType);\r
-}\r
-\r
-/*******************************************************************************\r
- * prvTraceExcludeOrIncludeKernelServiceFromTrace\r
- * \r
- * Includes or excludes all events that is related to the kernel service.\r
- ******************************************************************************/\r
-static void prvTraceExcludeOrIncludeKernelServiceFromTrace(traceKernelService kernelService, uint8_t flag)\r
-{\r
- switch(kernelService)\r
- {\r
- case TRACE_KERNEL_SERVICE_TASK_CREATE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_TASK);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_TASK);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_TASK);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_TASK);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_TASK_DELETE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_TASK);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_TASK);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_TASK_DELAY:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY_UNTIL);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_DELAY_UNTIL);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_PRIORITY_SET:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_PRIORITY_SET);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_PRIORITY_SET);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_TASK_SUSPEND:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_SUSPEND);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_SUSPEND);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_TASK_RESUME:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME_FROM_ISR);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(TASK_RESUME_FROM_ISR);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_QUEUE_CREATE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_QUEUE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_QUEUE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_QUEUE_DELETE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_QUEUE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_QUEUE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_QUEUE_SEND:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_QUEUE);\r
- \r
- \r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_QUEUE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_QUEUE_RECEIVE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_QUEUE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_QUEUE_PEEK:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_QUEUE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_MUTEX);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_QUEUE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_PEEK + TRACE_CLASS_MUTEX);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_MUTEX_CREATE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_MUTEX);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_MUTEX);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_MUTEX_DELETE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_MUTEX);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_MUTEX);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_MUTEX_GIVE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_MUTEX);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_MUTEX);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_MUTEX_TAKE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_MUTEX);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_SEMAPHORE_CREATE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_SEMAPHORE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_CREATE + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_SEMAPHORE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_SEMAPHORE_DELETE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_SEMAPHORE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_DELETE + TRACE_CLASS_SEMAPHORE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_SEMAPHORE_GIVE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_SEND + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_SEND_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- }\r
- break;\r
- case TRACE_KERNEL_SERVICE_SEMAPHORE_TAKE:\r
- if (flag)\r
- {\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- SET_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- }\r
- else\r
- {\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_BLOCK_ON_RECEIVE + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- CLEAR_EVENT_CODE_FLAG_ISEXCLUDED(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TRACE_CLASS_SEMAPHORE);\r
- }\r
- break;\r
- }\r
+ return sizeof(RecorderDataType);\r
}\r
\r
/******************************************************************************\r
- * vTraceExclude______FromTrace\r
+ * prvTraceTaskInstanceFinish.\r
*\r
- * Excludes a task or object from the trace.\r
- * This can be useful if some irrelevant task is very frequent and is "eating\r
- * up the buffer". This should be called after the task has been created, but \r
- * before starting the FreeRTOS scheduler.\r
+ * Private common function for the vTraceTaskInstanceFinishXXX functions.\r
+ * \r
*****************************************************************************/\r
-void vTraceExcludeQueueFromTrace(void* handle)\r
+void prvTraceTaskInstanceFinish(int8_t direct)\r
{\r
- SET_QUEUE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle));\r
-}\r
+ TaskInstanceStatusEvent* tis;\r
+ uint8_t dts45;\r
\r
-void vTraceExcludeSemaphoreFromTrace(void* handle)\r
-{\r
- SET_SEMAPHORE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle));\r
-}\r
-\r
-void vTraceExcludeMutexFromTrace(void* handle)\r
-{\r
- SET_MUTEX_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle));\r
-}\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
\r
-void vTraceExcludeTaskFromTrace(void* handle)\r
-{\r
- SET_TASK_FLAG_ISEXCLUDED(uxTaskGetTaskNumber(handle));\r
-}\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
+ {\r
+ dts45 = (uint8_t)prvTraceGetDTS(0xFF);\r
+ tis = (TaskInstanceStatusEvent*) xTraceNextFreeEventBufferSlot();\r
+ if (tis != NULL)\r
+ {\r
+ if (direct == 0)\r
+ tis->type = TASK_INSTANCE_FINISHED_NEXT_KSE;\r
+ else\r
+ tis->type = TASK_INSTANCE_FINISHED_DIRECT;\r
\r
-void vTraceExcludeKernelServiceFromTrace(traceKernelService kernelService)\r
-{\r
- prvTraceExcludeOrIncludeKernelServiceFromTrace(kernelService, 1);\r
+ tis->dts = dts45;\r
+ prvTraceUpdateCounters();\r
+ }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
}\r
\r
/******************************************************************************\r
- * vTraceInclude______InTrace\r
+ * vTraceTaskInstanceFinish(void)\r
*\r
- * Includes a task, object or kernel service in the trace. This is only\r
- * necessary if the task or object has been previously exluded.\r
- *****************************************************************************/\r
-void vTraceIncludeQueueInTrace(void* handle)\r
-{\r
- CLEAR_QUEUE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle));\r
-}\r
-\r
-void vTraceIncludeSemaphoreInTrace(void* handle)\r
-{\r
- CLEAR_SEMAPHORE_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle));\r
-}\r
-\r
-void vTraceIncludeMutexInTrace(void* handle)\r
-{\r
- CLEAR_MUTEX_FLAG_ISEXCLUDED(ucQueueGetQueueNumber(handle));\r
-}\r
-\r
-void vTraceIncludeTaskInTrace(void* handle)\r
-{\r
- CLEAR_TASK_FLAG_ISEXCLUDED(uxTaskGetTaskNumber(handle));\r
-}\r
-\r
-void vTraceIncludeKernelServiceInTrace(traceKernelService kernelService)\r
-{\r
- prvTraceExcludeOrIncludeKernelServiceFromTrace(kernelService, 0);\r
-}\r
-\r
-/*******************************************************************************\r
- * vTraceSetQueueName\r
+ * Marks the current task instance as finished on the next kernel call.\r
+ *\r
+ * If that kernel call is blocking, the instance ends after the blocking event\r
+ * and the corresponding return event is then the start of the next instance.\r
+ * If the kernel call is not blocking, the viewer instead splits the current\r
+ * fragment right before the kernel call, which makes this call the first event\r
+ * of the next instance.\r
*\r
- * Assigns a name to a FreeRTOS Queue, Semaphore or Mutex. This function should\r
- * be called right after creation of the queue/mutex/semaphore. If not using \r
- * this function, the queues/mutexes/semaphores will be presented by their\r
- * numeric handle only.\r
+ * See also USE_IMPLICIT_IFE_RULES in trcConfig.h\r
*\r
* Example:\r
- * actuatorQ = xQueueCreate(3, sizeof(QueueMessage));\r
- * vTraceSetQueueName(actuatorQ, "ActuatorQueue");\r
- ******************************************************************************/\r
-void vTraceSetQueueName(void* queue, const char* name)\r
+ *\r
+ * while(1)\r
+ * {\r
+ * xQueueReceive(CommandQueue, &command, timeoutDuration);\r
+ * processCommand(command);\r
+ * vTraceInstanceFinish();\r
+ * }\r
+ *\r
+ * Note: This is only supported in Tracealyzer tools v2.7 or later\r
+ *\r
+ *****************************************************************************/\r
+void vTraceTaskInstanceFinish(void)\r
{\r
- int t = ucQueueGetQueueType(queue); \r
- vTraceSetObjectName(TraceObjectClassTable[t], \r
- (objectHandleType)ucQueueGetQueueNumber(queue), name);\r
+ prvTraceTaskInstanceFinish(0);\r
}\r
\r
-\r
/******************************************************************************\r
- * vTraceTaskInstanceIsFinished\r
- * \r
- * This defines an explicit Instance Finish Event for the current task. It tells \r
- * the recorder that the current instance of this task is finished at the next \r
- * kernel call of the task, e.g., a taskDelay or a queue receive. This function \r
- * should be called right before the api function call considered to be the end \r
- * of the current task instance, i.e., the Instance Finish Event.\r
+ * vTraceTaskInstanceFinishDirect(void)\r
+ *\r
+ * Marks the current task instance as finished at this very instant.\r
+ * This makes the viewer to splits the current fragment at this point and begin\r
+ * a new actor instance.\r
+ *\r
+ * See also USE_IMPLICIT_IFE_RULES in trcConfig.h\r
+ *\r
+ * Example:\r
+ *\r
+ * This example will generate two instances for each loop iteration.\r
+ * The first instance ends at vTraceInstanceFinishDirect(), while the second\r
+ * instance ends at the next xQueueReceive call.\r
+ *\r
+ * while (1)\r
+ * {\r
+ * xQueueReceive(CommandQueue, &command, timeoutDuration);\r
+ * ProcessCommand(command);\r
+ * vTraceInstanceFinishDirect();\r
+ * DoSometingElse();\r
+ * vTraceInstanceFinish();\r
+ * }\r
+ *\r
+ * Note: This is only supported in Tracealyzer tools v2.7 or later\r
+ *\r
*****************************************************************************/\r
-void vTraceTaskInstanceIsFinished()\r
+void vTraceTaskInstanceFinishDirect(void)\r
{\r
- if (handle_of_last_logged_task)\r
- {\r
- SET_TASK_FLAG_MARKIFE(handle_of_last_logged_task); \r
- }\r
+ prvTraceTaskInstanceFinish(1);\r
}\r
\r
/*******************************************************************************\r
- * vvTraceTaskSkipDefaultInstanceFinishedEvents\r
- *\r
- * This is useful if there are implicit Instance Finish Events, such as \r
- * vTaskDelayUntil or xQueueReceive, in a task where an explicit Instance Finish \r
- * Event has been defined. This function tells the recorder that only the \r
- * explicitly defined functions, using vTraceTaskInstanceIsFinished, should be\r
- * treated as Instance Finish Events for this task. The implicit Instance Finish \r
- * Events are thus disregarded for the calling task.\r
- ******************************************************************************/\r
-void vTraceTaskSkipDefaultInstanceFinishedEvents()\r
-{ \r
- if (handle_of_last_logged_task)\r
- {\r
- PROPERTY_TASK_IFE_SERVICECODE(handle_of_last_logged_task) = \r
- RESERVED_DUMMY_CODE;\r
- }\r
-}\r
-\r
-/*******************************************************************************\r
- * Interrupt recording functions \r
+ * Interrupt recording functions\r
******************************************************************************/\r
\r
#if (INCLUDE_ISR_TRACING == 1)\r
\r
/*******************************************************************************\r
* vTraceSetISRProperties\r
- * \r
+ *\r
* Registers an Interrupt Service Routine in the recorder library, This must be\r
- * called before using vTraceStoreISRBegin to store ISR events. This is \r
- * typically called in the startup of the system, before the scheduler is \r
+ * called before using vTraceStoreISRBegin to store ISR events. This is\r
+ * typically called in the startup of the system, before the scheduler is\r
* started.\r
*\r
* Example:\r
- * #define ID_ISR_TIMER1 1 // lowest valid ID is 1\r
- * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt\r
- * ...\r
- * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1);\r
- * ...\r
- * void ISR_handler()\r
- * {\r
- * vTraceStoreISRBegin(ID_OF_ISR_TIMER1);\r
- * ...\r
- * vTraceStoreISREnd();\r
- * }\r
- *\r
- * NOTE: To safely record ISRs, you need to make sure that all traced \r
- * interrupts actually are disabled by trcCRITICAL_SECTION_BEGIN(), which \r
- * typically is mapped to portENTER_CRITICAL(), which uses the macro \r
- * portDISABLE_INTERRUPTS. However, in some ports of FreeRTOS and depending on \r
- * FreeRTOS configuration, this does not disable high priority interrupts!\r
+ * #define ID_ISR_TIMER1 1 // lowest valid ID is 1\r
+ * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt\r
+ * ...\r
+ * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1);\r
+ * ...\r
+ * void ISR_handler()\r
+ * {\r
+ * vTraceStoreISRBegin(ID_OF_ISR_TIMER1);\r
+ * ...\r
+ * vTraceStoreISREnd(0);\r
+ * }\r
+ *\r
+ * NOTE: To safely record ISRs, you need to make sure that all traced\r
+ * interrupts actually are disabled by trcCRITICAL_SECTION_BEGIN(). However,\r
+ * in some ports this does not disable high priority interrupts!\r
* If an ISR calls vTraceStoreISRBegin while the recorder is busy, it will\r
* stop the recording and give an error message.\r
******************************************************************************/\r
void vTraceSetISRProperties(objectHandleType handle, const char* name, char priority)\r
{\r
- vTraceSetObjectName(TRACE_CLASS_ISR, handle, name);\r
- vTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority);\r
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceSetISRProperties: Invalid value for handle", );\r
+ TRACE_ASSERT(name != NULL, "vTraceSetISRProperties: name == NULL", );\r
+\r
+ vTraceSetObjectName(TRACE_CLASS_ISR, handle, name);\r
+ vTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority);\r
}\r
\r
+#if (SELECTED_PORT == PORT_ARM_CortexM)\r
+/******************************************************************************\r
+ * (Advanced...)\r
+ *\r
+ * ISR_TAILCHAINING_THRESHOLD (For Cortex-M devices only)\r
+ *\r
+ * ARM Cortex-M devices may execute ISRs back-to-back (tail-chained) without\r
+ * resuming the previous context in between. Since this is decided in\r
+ * hardware, we can only detect this indirectly, in the following manner:\r
+ *\r
+ * When entering vTraceStoreISRBegin, we check the number of CPU cycles since\r
+ * the last return of vTraceStoreISREnd. If less or equal to the constant\r
+ * ISR_TAILCHAINING_THRESHOLD it is assumed that the ISRs executed back-to-back\r
+ * (tail-chained). In that case, the previously stored RESUME event\r
+ * (pointed to by ptrLastISRExitEvent) is then deleted to avoid showing a\r
+ * fragment of the previous context in between the ISR events. The delete is\r
+ * made by replacing the event code with a XTS16L event, that serves to keep\r
+ * the differential timestamp from the earlier event.\r
+ *\r
+ * The value of ISR_TAILCHAINING_THRESHOLD depends on the interrupt latency of\r
+ * the processor, on the compiler and on the compiler settings, but should be\r
+ * around 70 cycles. The default value is 66 cycles, which should be correct when\r
+ * using GCC with optimizations disabled (-O0) and Cortex-M3/M4.\r
+ *\r
+ * To measure this value, see MEASURE_ISR_TAILCHAINING_THRESHOLD below.\r
+ *\r
+ * If this value set too low, tail-chained ISRs will incorrectly be shown\r
+ * separated, with a short fragment of the previous task or ISR in between.\r
+ * If this value is set too high, you get the opposite effect - separate ISRs\r
+ * will appear to execute tail-chained and will appear to have higher execution\r
+ * time and response time (maximum ISR_TAILCHAINING_THRESHOLD cycles more).\r
+ *\r
+ * Read the blog post explaining this on our website:\r
+ * http://percepio.com/2014/05/06/sw-based-exc-tracing-arm-cortex-m/\r
+ *\r
+ *****************************************************************************/\r
+#define ISR_TAILCHAINING_THRESHOLD 66\r
+\r
+uint8_t* ptrLastISRExitEvent = NULL;\r
+uint32_t DWTCycleCountAtLastISRExit = 0;\r
+\r
+/******************************************************************************\r
+ * (Advanced...)\r
+ *\r
+ * MEASURE_ISR_TAILCHAINING_THRESHOLD (For Cortex-M devices only)\r
+ *\r
+ * Allows for calibrating the value of ISR_TAILCHAINING_THRESHOLD (see above).\r
+ *\r
+ * This is intended to measure the minimum number of clock cycles from the end\r
+ * of vTraceStoreISREnd to the beginning of the following vTraceStoreISRBegin.\r
+ * For this purpose, we assume a test setup using the SysTick interrupt, which\r
+ * is available on most Cortex-M devices and typically used by the RTOS kernel.\r
+ * To do the measurement, follow these steps:\r
+ *\r
+ * 1. Make sure MEASURE_ISR_TAILCHAINING_THRESHOLD is enabled (defined as 1)\r
+ *\r
+ * 2. Temporarily replace your SysTick handler with the following:\r
+ *\r
+ * void xPortSysTickHandler( void )\r
+ * {\r
+ * vTraceStoreISRBegin(1);\r
+ * vTraceStoreISREnd(0);\r
+ * }\r
+ *\r
+ * 3. To make sure that the ISRs execute back-to-back, increase the OS tick\r
+ * frequency to a very high level so that the OS tick interrupt execute\r
+ * continuously with no application tasks in between, e.g. 10 MHz.\r
+ *\r
+ * 4. Put a breakpoint in the highest priority task and make sure it is not\r
+ * reached. This means that the SysTick handler is executing at maximum rate\r
+ * and thereby tail-chained, where the interrupt latency is 6 cycles.\r
+ *\r
+ * 5. Let the system run without breakpoints and inspect the value of\r
+ * threshold_low_watermark. This is the minimum total latency observed.\r
+ * The hardware latency is 6 clock cycles due to the tail-chaining, so the\r
+ * software latency (SL) is then SL = threshold_low_watermark - 6.\r
+ *\r
+ * The threshold value ISR_TAILCHAINING_THRESHOLD should be SL + 2 * HL, where\r
+ * HL is the normal hardware interrupt latency, i.e., the number of CPU\r
+ * cycles to enter or exit the exception handler for an exception in task\r
+ * context. The HL value is 12-16 depending on core, as shown below.\r
+ *\r
+ * Values for ISR_TAILCHAINING_THRESHOLD, assuming SL = 42\r
+ * Cortex-M3 and M4 (HL = 12): 66 cycles\r
+ * Cortex-M0 (HL = 16): 74 cycles\r
+ * Cortex-M0+ (HL = 15): 72 cycles\r
+ *\r
+ * If the ISR_TAILCHAINING_THRESHOLD value is set too low, some tail-chained\r
+ * ISRs be shown separated, with a short fragment of the previous context\r
+ * in between. On the other hand, if the value is set too high, ISRs that \r
+ * actually are separated may appear to execute back-to-back (tail-chained).\r
+ *\r
+ * Read the blog post explaining this on our website:\r
+ * http://percepio.com/2014/05/06/sw-based-exc-tracing-arm-cortex-m/\r
+ *\r
+ *****************************************************************************/\r
+#define MEASURE_ISR_TAILCHAINING_THRESHOLD 1\r
+\r
+#if (MEASURE_ISR_TAILCHAINING_THRESHOLD == 1)\r
+volatile uint32_t threshold_low_watermark = 2000000000;\r
+#endif\r
+\r
+#endif\r
+\r
/*******************************************************************************\r
* vTraceStoreISRBegin\r
- * \r
+ *\r
* Registers the beginning of an Interrupt Service Routine.\r
*\r
* Example:\r
- * #define ID_ISR_TIMER1 1 // lowest valid ID is 1\r
- * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt\r
- * ...\r
- * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1);\r
- * ...\r
- * void ISR_handler()\r
- * {\r
- * vTraceStoreISRBegin(ID_OF_ISR_TIMER1);\r
- * ...\r
- * vTraceStoreISREnd();\r
- * }\r
- *\r
- * NOTE: You need to make sure that any traced interrupts actually are \r
- * disabled by trcCRITICAL_SECTION_BEGIN(), i.e., taskENTER_CRITICAL() which\r
- * uses portDISABLE_INTERRUPTS(). \r
- * In some ports of FreeRTOS, this does not disable high-priority interrupts,\r
- * i.e., with priorities above configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
- * If an invalid call to vTraceStoreISRBegin is detected (i.e., that preempted\r
- * a critical section of the recorder) this will generate a recorder error \r
- * using vTraceError.\r
+ * #define ID_ISR_TIMER1 1 // lowest valid ID is 1\r
+ * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt\r
+ * ...\r
+ * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1);\r
+ * ...\r
+ * void ISR_handler()\r
+ * {\r
+ * vTraceStoreISRBegin(ID_OF_ISR_TIMER1);\r
+ * ...\r
+ * vTraceStoreISREnd(0);\r
+ * }\r
+ *\r
******************************************************************************/\r
void vTraceStoreISRBegin(objectHandleType handle)\r
{\r
- uint16_t dts4;\r
- TSEvent* ts = NULL;\r
-\r
- if (recorder_busy)\r
- {\r
- vTraceError("Illegal call to vTraceStoreISRBegin/End");\r
- return;\r
- }\r
- if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
- { \r
- trcCRITICAL_SECTION_BEGIN();\r
- dts4 = (uint16_t)prvTraceGetDTS(0xFFFF);\r
-\r
- if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ \r
- { \r
-\r
- if (nISRactive < MAX_ISR_NESTING)\r
- { \r
- isrstack[nISRactive] = handle;\r
- nISRactive++; \r
- ts = (TSEvent*)xTraceNextFreeEventBufferSlot();\r
- if (ts != NULL)\r
- {\r
- ts->type = TS_ISR_BEGIN;\r
- ts->dts = dts4;\r
- ts->objHandle = handle;\r
- prvTraceUpdateCounters(); \r
- }\r
- }\r
- else\r
- { \r
- /* This should not occur unless something is very wrong */ \r
- vTraceError("Too many nested interrupts!");\r
- } \r
- }\r
- trcCRITICAL_SECTION_END(); \r
- }\r
-}\r
+ uint16_t dts4;\r
+ #if (SELECTED_PORT == PORT_ARM_CortexM)\r
+ uint32_t CPUCyclesSinceLastISRExit = REG_DWT_CYCCNT - DWTCycleCountAtLastISRExit;\r
+ #endif\r
+ TSEvent* ts;\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
\r
+ ts = NULL;\r
\r
#if (SELECTED_PORT == PORT_ARM_CortexM)\r
+ if (DWTCycleCountAtLastISRExit > 0)\r
+ {\r
+ #if (MEASURE_ISR_TAILCHAINING_THRESHOLD == 1)\r
+ /* Allows for verifying the value of ISR_TAILCHAINING_THRESHOLD */\r
+ if (CPUCyclesSinceLastISRExit < threshold_low_watermark)\r
+ {\r
+ threshold_low_watermark = CPUCyclesSinceLastISRExit;\r
+ }\r
+ #endif\r
\r
-int tailchain_irq_pending(void);\r
-\r
-/*******************************************************************************\r
- * tailchain_irq_pending\r
- *\r
- * For Cortex-M chips only. Returns 1 if an interrupt is pending, by checking \r
- * the 8 NVIC IRQ pend registers at 0xE000E200 to 0xE000E21C. Returns 0 if no \r
- * interrupt is pending. This is used to predict tailchaining of ISRs.\r
- ******************************************************************************/\r
-int tailchain_irq_pending(void)\r
-{\r
- uint32_t* pend_reg = ((uint32_t*)0xE000E200);\r
- int i;\r
-\r
- for (i=0; i<8; i++)\r
- {\r
- if (pend_reg[i] != 0)\r
- {\r
- return 1;\r
- }\r
- }\r
- return 0; \r
-}\r
+ if (CPUCyclesSinceLastISRExit <= ISR_TAILCHAINING_THRESHOLD)\r
+ {\r
+ /* This is judged to be a case of ISR tail-chaining since the\r
+ number of cycles since the last vTraceStoreISREnd is shorter or equal to\r
+ the threshold (ISR_TAILCHAINING_THRESHOLD) */\r
+\r
+ if (ptrLastISRExitEvent != NULL)\r
+ {\r
+ /* Overwrite the last ISR exit event with a "neutral" event that only\r
+ accounts for the time passed */\r
+ *ptrLastISRExitEvent = XTS16L;\r
+ }\r
+ }\r
\r
+ }\r
#endif\r
\r
+ if (recorder_busy)\r
+ {\r
+ vTraceError("Illegal call to vTraceStoreISRBegin, recorder busy!");\r
+ return;\r
+ }\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
+ {\r
+\r
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid value for handle", );\r
+\r
+ dts4 = (uint16_t)prvTraceGetDTS(0xFFFF);\r
+\r
+ if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */\r
+ {\r
+ if (nISRactive < MAX_ISR_NESTING)\r
+ {\r
+ uint8_t hnd8 = prvTraceGet8BitHandle(handle);\r
+ isrstack[nISRactive] = handle;\r
+ nISRactive++;\r
+ ts = (TSEvent*)xTraceNextFreeEventBufferSlot();\r
+ if (ts != NULL)\r
+ {\r
+ ts->type = TS_ISR_BEGIN;\r
+ ts->dts = dts4;\r
+ ts->objHandle = hnd8;\r
+ prvTraceUpdateCounters();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* This should not occur unless something is very wrong */\r
+ vTraceError("Too many nested interrupts!");\r
+ }\r
+ }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
+}\r
+\r
/*******************************************************************************\r
* vTraceStoreISREnd\r
- * \r
- * Registers the end of an Interrupt Service Routine. \r
+ *\r
+ * Registers the end of an Interrupt Service Routine.\r
+ *\r
+ * The parameter pendingISR indicates if the interrupt has requested a\r
+ * task-switch (= 1) or if the interrupt returns to the earlier context (= 0)\r
*\r
* Example:\r
- * #define ID_ISR_TIMER1 1 // lowest valid ID is 1\r
- * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt\r
- * ...\r
- * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1);\r
- * ...\r
- * void ISR_handler()\r
- * {\r
- * vTraceStoreISRBegin(ID_OF_ISR_TIMER1);\r
- * ...\r
- * vTraceStoreISREnd();\r
- * }\r
- *\r
- * NOTE: You need to make sure that any traced interrupts actually are \r
- * disabled by trcCRITICAL_SECTION_BEGIN(), i.e., taskENTER_CRITICAL() which\r
- * uses portDISABLE_INTERRUPTS(). \r
- * In some ports of FreeRTOS, this does not disable high-priority interrupts,\r
- * i.e., with priorities above configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
- * If an invalid call to vTraceStoreISREnd is detected (i.e., that preempted\r
- * a critical section of the recorder) this will generate a recorder error \r
- * using vTraceError.\r
+ *\r
+ * #define ID_ISR_TIMER1 1 // lowest valid ID is 1\r
+ * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt\r
+ * ...\r
+ * vTraceSetISRProperties(ID_ISR_TIMER1, "ISRTimer1", PRIO_OF_ISR_TIMER1);\r
+ * ...\r
+ * void ISR_handler()\r
+ * {\r
+ * vTraceStoreISRBegin(ID_OF_ISR_TIMER1);\r
+ * ...\r
+ * vTraceStoreISREnd(0);\r
+ * }\r
+ *\r
******************************************************************************/\r
-void vTraceStoreISREnd(void)\r
+void vTraceStoreISREnd(int pendingISR)\r
{\r
- TSEvent* ts;\r
- uint16_t dts5;\r
-\r
- if (recorder_busy)\r
- {\r
- vTraceError("Illegal call to vTraceStoreISRBegin/End");\r
- return;\r
- }\r
- \r
- if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
- {\r
- #if (SELECTED_PORT == PORT_ARM_CortexM)\r
- if (tailchain_irq_pending() > 0)\r
- {\r
- nISRactive--; /* If an IRQ strikes exactly here, the resulting \r
- ISR tailchaining is not detected. The trace instead shows a very \r
- short fragment of the earlier preempted task/ISR, and then the new\r
- ISR begins. */\r
- return;\r
- }\r
- #endif\r
- \r
- trcCRITICAL_SECTION_BEGIN();\r
- dts5 = (uint16_t)prvTraceGetDTS(0xFFFF);\r
-\r
- if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ \r
- { \r
- ts = (TSEvent*)xTraceNextFreeEventBufferSlot();\r
- if (ts != NULL)\r
- {\r
- if (nISRactive > 1)\r
- {\r
- /* return to another isr */\r
- ts->type = TS_ISR_RESUME;\r
- ts->objHandle = isrstack[nISRactive];\r
- }\r
- else\r
- {\r
- /* return to task */\r
- ts->type = TS_TASK_RESUME;\r
- ts->objHandle = handle_of_last_logged_task;\r
- }\r
- ts->dts = dts5;\r
- nISRactive--;\r
- prvTraceUpdateCounters();\r
- }\r
- }\r
- trcCRITICAL_SECTION_END(); \r
- }\r
+ TSEvent* ts;\r
+ uint16_t dts5;\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
+\r
+ if (recorder_busy)\r
+ {\r
+ vTraceError("Illegal call to vTraceStoreISREnd, recorder busy!");\r
+ return;\r
+ }\r
+\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ if (pendingISR == 0)\r
+ {\r
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
+ {\r
+ uint8_t hnd8, type;\r
+ dts5 = (uint16_t)prvTraceGetDTS(0xFFFF);\r
+\r
+ if (nISRactive > 1)\r
+ {\r
+ /* return to another isr */\r
+ type = TS_ISR_RESUME;\r
+ hnd8 = prvTraceGet8BitHandle(isrstack[nISRactive]);\r
+ }\r
+ else\r
+ {\r
+ /* return to task */\r
+ type = TS_TASK_RESUME;\r
+ hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);\r
+ }\r
+\r
+ ts = (TSEvent*)xTraceNextFreeEventBufferSlot();\r
+ if (ts != NULL)\r
+ {\r
+ ts->type = type;\r
+ ts->objHandle = hnd8;\r
+ ts->dts = dts5;\r
+ prvTraceUpdateCounters();\r
+ }\r
+\r
+ #if (SELECTED_PORT == PORT_ARM_CortexM)\r
+ /* Remember the last ISR exit event, as the event needs to be modified in case of a following ISR entry (if tail-chained ISRs) */\r
+ ptrLastISRExitEvent = (uint8_t*)ts;\r
+ #endif\r
+ }\r
+ }\r
+ nISRactive--;\r
+\r
+ #if (SELECTED_PORT == PORT_ARM_CortexM)\r
+ DWTCycleCountAtLastISRExit = REG_DWT_CYCCNT;\r
+ #endif\r
+\r
+ trcCRITICAL_SECTION_END();\r
}\r
\r
#else\r
/* ISR tracing is turned off */\r
void vTraceIncreaseISRActive(void)\r
{\r
- if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
- nISRactive++;\r
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
+ nISRactive++;\r
}\r
\r
-void vTraceDecreaseISRActive(objectHandleType handle)\r
+void vTraceDecreaseISRActive(void)\r
{\r
- if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
- nISRactive--;\r
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)\r
+ nISRactive--;\r
}\r
#endif\r
\r
\r
-/*******************************************************************************\r
- * User Event functions\r
- ******************************************************************************/\r
+/********************************************************************************/\r
+/* User Event functions */\r
+/********************************************************************************/\r
\r
#if (INCLUDE_USER_EVENTS == 1)\r
\r
- /******************************************************************************\r
- * vTraceUserEvent\r
- *\r
- * Basic user event (Standard and Professional Edition only)\r
- * \r
- * Generates a User Event with a text label. The label is created/looked up\r
- * in the symbol table using xTraceOpenLabel.\r
- ******************************************************************************/\r
-void vTraceUserEvent(traceLabel eventLabel)\r
-{\r
- UserEvent* ue;\r
- uint8_t dts1;\r
-\r
- if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive ) && handle_of_last_logged_task)\r
- {\r
- trcCRITICAL_SECTION_BEGIN();\r
-\r
- dts1 = (uint8_t)prvTraceGetDTS(0xFF);\r
-\r
- if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ \r
- { \r
- ue = (UserEvent*) xTraceNextFreeEventBufferSlot();\r
- if (ue != NULL)\r
- {\r
- ue->dts = dts1;\r
- ue->type = USER_EVENT;\r
- ue->payload = eventLabel;\r
- prvTraceUpdateCounters();\r
- }\r
- }\r
- trcCRITICAL_SECTION_END(); \r
- }\r
-}\r
-\r
-/*** Locally used in vTracePrintF ***/\r
-\r
-/* one word (32 bit) is required for the USER_EVENT entry, 8 words \r
-(8*32 bit = 32 byte) available for argument data */\r
-#define MAX_ARG_SIZE (4+32) \r
-\r
-static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value);\r
-static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value);\r
-static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value);\r
-\r
-#if (INCLUDE_FLOAT_SUPPORT)\r
-static uint8_t writeFloat(void * buffer, uint8_t i, float value);\r
-static uint8_t writeDouble(void * buffer, uint8_t i, double value);\r
-#endif\r
-\r
+#define MAX_ARG_SIZE (4+32)\r
/*** Locally used in vTracePrintF ***/\r
static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value)\r
-{ \r
- \r
- if (i >= MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- }\r
+{\r
+ TRACE_ASSERT(buffer != NULL, "writeInt8: buffer == NULL", 0);\r
+\r
+ if (i >= MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
\r
- ((uint8_t*)buffer)[i] = value;\r
+ ((uint8_t*)buffer)[i] = value;\r
\r
if (i + 1 > MAX_ARG_SIZE)\r
{\r
return 255;\r
}\r
\r
- return i + 1;\r
+ return i + 1;\r
}\r
\r
/*** Locally used in vTracePrintF ***/\r
static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value)\r
-{ \r
- /* Align to multiple of 2 */\r
- while ((i % 2) != 0)\r
- {\r
+{\r
+ TRACE_ASSERT(buffer != NULL, "writeInt16: buffer == NULL", 0);\r
+\r
+ /* Align to multiple of 2 */\r
+ while ((i % 2) != 0)\r
+ {\r
if (i >= MAX_ARG_SIZE)\r
{\r
return 255;\r
}\r
- \r
- ((uint8_t*)buffer)[i] = 0;\r
- i++; \r
- }\r
- \r
- if (i + 2 > MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- }\r
-\r
- ((uint16_t*)buffer)[i/2] = value;\r
-\r
- return i + 2;\r
+\r
+ ((uint8_t*)buffer)[i] = 0;\r
+ i++;\r
+ }\r
+\r
+ if (i + 2 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
+\r
+ ((uint16_t*)buffer)[i/2] = value;\r
+\r
+ return i + 2;\r
}\r
\r
/*** Locally used in vTracePrintF ***/\r
static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value)\r
{\r
- \r
- /* A 32 bit value should begin at an even 4-byte address */\r
- while ((i % 4) != 0)\r
- {\r
+ TRACE_ASSERT(buffer != NULL, "writeInt32: buffer == NULL", 0);\r
+\r
+ /* A 32 bit value should begin at an even 4-byte address */\r
+ while ((i % 4) != 0)\r
+ {\r
if (i >= MAX_ARG_SIZE)\r
{\r
return 255;\r
}\r
- \r
- ((uint8_t*)buffer)[i] = 0;\r
- i++;\r
- }\r
- \r
- if (i + 4 > MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- } \r
-\r
- ((uint32_t*)buffer)[i/4] = value;\r
-\r
- return i + 4;\r
+\r
+ ((uint8_t*)buffer)[i] = 0;\r
+ i++;\r
+ }\r
+\r
+ if (i + 4 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
+\r
+ ((uint32_t*)buffer)[i/4] = value;\r
+\r
+ return i + 4;\r
}\r
\r
#if (INCLUDE_FLOAT_SUPPORT)\r
/*** Locally used in vTracePrintF ***/\r
static uint8_t writeFloat(void * buffer, uint8_t i, float value)\r
{\r
- /* A 32 bit value should begin at an even 4-byte address */\r
- while ((i % 4) != 0)\r
- {\r
+ TRACE_ASSERT(buffer != NULL, "writeFloat: buffer == NULL", 0);\r
+\r
+ /* A 32 bit value should begin at an even 4-byte address */\r
+ while ((i % 4) != 0)\r
+ {\r
if (i >= MAX_ARG_SIZE)\r
{\r
return 255;\r
}\r
\r
- ((uint8_t*)buffer)[i] = 0;\r
- i++;\r
- }\r
+ ((uint8_t*)buffer)[i] = 0;\r
+ i++;\r
+ }\r
+\r
+ if (i + 4 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
\r
- if (i + 4 > MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- } \r
+ ((float*)buffer)[i/4] = value;\r
\r
- ((float*)buffer)[i/4] = value;\r
- \r
- return i + 4;\r
+ return i + 4;\r
}\r
\r
/*** Locally used in vTracePrintF ***/\r
static uint8_t writeDouble(void * buffer, uint8_t i, double value)\r
{\r
- uint32_t * dest = buffer;\r
- uint32_t * src = (void*)&value;\r
- /* The double is written as two 32 bit values, and should begin at an even \r
- 4-byte address (to avoid having to align with 8 byte) */\r
- while (i % 4 != 0)\r
- {\r
+ uint32_t * dest;\r
+ uint32_t * src = (uint32_t*)&value;\r
+\r
+ TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0);\r
+\r
+ /* The double is written as two 32 bit values, and should begin at an even\r
+ 4-byte address (to avoid having to align with 8 byte) */\r
+ while (i % 4 != 0)\r
+ {\r
if (i >= MAX_ARG_SIZE)\r
{\r
return 255;\r
}\r
\r
- ((uint8_t*)buffer)[i] = 0;\r
- i++; \r
- }\r
- \r
- if (i + 8 > MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- } \r
- \r
- dest[i/4+0] = src[0];\r
- dest[i/4+1] = src[1];\r
- \r
- return i + 8;\r
+ ((uint8_t*)buffer)[i] = 0;\r
+ i++;\r
+ }\r
+\r
+ if (i + 8 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
+\r
+ dest = &(((uint32_t *)buffer)[i/4]);\r
+\r
+ dest[0] = src[0];\r
+ dest[1] = src[1];\r
+\r
+ return i + 8;\r
}\r
\r
#endif\r
\r
- /******************************************************************************\r
- * vTracePrintF \r
- * \r
+/*******************************************************************************\r
+ * prvTraceUserEventFormat\r
+ *\r
+ * Parses the format string and stores the arguments in the buffer.\r
+ ******************************************************************************/\r
+static uint8_t prvTraceUserEventFormat(const char* formatStr, va_list vl, uint8_t* buffer, uint8_t byteOffset)\r
+{\r
+ uint16_t formatStrIndex = 0;\r
+ uint8_t argCounter = 0;\r
+ uint8_t i = byteOffset;\r
+\r
+ while (formatStr[formatStrIndex] != '\0')\r
+ {\r
+ if (formatStr[formatStrIndex] == '%')\r
+ {\r
+ argCounter++;\r
+\r
+ if (argCounter > 15)\r
+ {\r
+ vTraceError("vTracePrintF - Too many arguments, max 15 allowed!");\r
+ return 0;\r
+ }\r
+\r
+ /*******************************************************************************\r
+ * These below code writes raw data (primitive datatypes) in the event buffer,\r
+ * instead of the normal event structs (where byte 0 is event type).\r
+ * These data entries must never be interpreted as real event data, as the type\r
+ * field would be misleading since used for payload data.\r
+ *\r
+ * The correctness of this encoding depends on two mechanisms:\r
+ *\r
+ * 1. An initial USER_EVENT, which type code tells the number of 32-bit data\r
+ * entires that follows. (code - USER_EVENT = number of data entries).\r
+ * Note that a data entry corresponds to the slots that normally corresponds to\r
+ * one (1) event, i.e., 32 bits. vTracePrintF may encode several pieces of data\r
+ * in one data entry, e.g., two 16-bit values or four 8-bit values, one 16-bit\r
+ * value followed by two 8-bit values, etc.\r
+ *\r
+ * 2. A two-phase commit procedure, where the USER_EVENT and data entries are\r
+ * written to a local buffer at first, and when all checks are OK then copied to\r
+ * the main event buffer using a fast memcpy. The event code is finalized as the\r
+ * very last step. Before that step, the event code indicates an unfinished\r
+ * event, which causes it to be ignored and stop the loading of the file (since\r
+ * an unfinished event is the last event in the trace).\r
+ *******************************************************************************/\r
+ formatStrIndex++;\r
+\r
+ while ((formatStr[formatStrIndex] >= '0' && formatStr[formatStrIndex] <= '9') || formatStr[formatStrIndex] == '#' || formatStr[formatStrIndex] == '.')\r
+ formatStrIndex++;\r
+\r
+ if (formatStr[formatStrIndex] != '\0')\r
+ {\r
+ switch (formatStr[formatStrIndex])\r
+ {\r
+ case 'd': i = writeInt32( buffer,\r
+ i,\r
+ (uint32_t)va_arg(vl, uint32_t));\r
+ break;\r
+ case 'x':\r
+ case 'X':\r
+ case 'u': i = writeInt32( buffer,\r
+ i,\r
+ (uint32_t)va_arg(vl, uint32_t));\r
+ break;\r
+ case 's': i = writeInt16( buffer,\r
+ i,\r
+ (uint16_t)xTraceOpenLabel((char*)va_arg(vl, char*)));\r
+ break;\r
+\r
+#if (INCLUDE_FLOAT_SUPPORT)\r
+ /* Yes, "double" as type also in the float\r
+ case. This since "float" is promoted into "double"\r
+ by the va_arg stuff. */\r
+ case 'f': i = writeFloat( buffer,\r
+ i,\r
+ (float)va_arg(vl, double));\r
+ break;\r
+#else\r
+ /* No support for floats, but attempt to store a float user event\r
+ avoid a possible crash due to float reference. Instead store the\r
+ data on uint_32 format (will not be displayed anyway). This is just\r
+ to keep va_arg and i consistent. */\r
+\r
+ case 'f': i = writeInt32( buffer,\r
+ i,\r
+ (uint32_t)va_arg(vl, double));\r
+ break;\r
+#endif\r
+ case 'l':\r
+ formatStrIndex++;\r
+ switch (formatStr[formatStrIndex])\r
+ {\r
+#if (INCLUDE_FLOAT_SUPPORT)\r
+ case 'f': i = writeDouble(buffer,\r
+ i,\r
+ (double)va_arg(vl, double));\r
+ break;\r
+#else\r
+ /* No support for floats, but attempt to store a float user event\r
+ avoid a possible crash due to float reference. Instead store the\r
+ data on uint_32 format (will not be displayed anyway). This is just\r
+ to keep va_arg and i consistent. */\r
+ case 'f': i = writeInt32( buffer, /* In this case, the value will not be shown anyway */\r
+ i,\r
+ (uint32_t)va_arg(vl, double));\r
+\r
+ i = writeInt32( buffer, /* Do it twice, to write in total 8 bytes */\r
+ i,\r
+ (uint32_t)va_arg(vl, double));\r
+ break;\r
+#endif\r
+\r
+ }\r
+ break;\r
+ case 'h':\r
+ formatStrIndex++;\r
+ switch (formatStr[formatStrIndex])\r
+ {\r
+ case 'd': i = writeInt16( buffer,\r
+ i,\r
+ (uint16_t)va_arg(vl, uint32_t));\r
+ break;\r
+ case 'u': i = writeInt16( buffer,\r
+ i,\r
+ (uint16_t)va_arg(vl, uint32_t));\r
+ break;\r
+ }\r
+ break;\r
+ case 'b':\r
+ formatStrIndex++;\r
+ switch (formatStr[formatStrIndex])\r
+ {\r
+ case 'd': i = writeInt8( buffer,\r
+ i,\r
+ (uint8_t)va_arg(vl, uint32_t));\r
+ break;\r
+\r
+ case 'u': i = writeInt8( buffer,\r
+ i,\r
+ (uint8_t)va_arg(vl, uint32_t));\r
+ break;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ break;\r
+ }\r
+ formatStrIndex++;\r
+ if (i == 255)\r
+ {\r
+ vTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!");\r
+ return 0;\r
+ }\r
+ }\r
+ return (i+3)/4;\r
+}\r
+\r
+#if (USE_SEPARATE_USER_EVENT_BUFFER == 1)\r
+\r
+/*******************************************************************************\r
+ * prvTraceClearChannelBuffer\r
+ *\r
+ * Clears a number of items in the channel buffer, starting from nextSlotToWrite.\r
+ ******************************************************************************/\r
+static void prvTraceClearChannelBuffer(uint32_t count)\r
+{\r
+ uint32_t slots;\r
+\r
+ TRACE_ASSERT(USER_EVENT_BUFFER_SIZE >= count,\r
+ "prvTraceClearChannelBuffer: USER_EVENT_BUFFER_SIZE is too small to handle this event.", );\r
+\r
+ /* Check if we're close to the end of the buffer */\r
+ if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > USER_EVENT_BUFFER_SIZE)\r
+ {\r
+ slots = USER_EVENT_BUFFER_SIZE - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */\r
+ (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, slots);\r
+ (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[0], 0, (count - slots));\r
+ }\r
+ else\r
+ (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, count);\r
+}\r
+\r
+/*******************************************************************************\r
+ * prvTraceCopyToDataBuffer\r
+ *\r
+ * Copies a number of items to the data buffer, starting from nextSlotToWrite.\r
+ ******************************************************************************/\r
+static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count)\r
+{\r
+ TRACE_ASSERT(data != NULL,\r
+ "prvTraceCopyToDataBuffer: data == NULL.", );\r
+ TRACE_ASSERT(count <= USER_EVENT_BUFFER_SIZE,\r
+ "prvTraceCopyToDataBuffer: USER_EVENT_BUFFER_SIZE is too small to handle this event.", );\r
+\r
+ uint32_t slots;\r
+ /* Check if we're close to the end of the buffer */\r
+ if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > USER_EVENT_BUFFER_SIZE)\r
+ {\r
+ slots = USER_EVENT_BUFFER_SIZE - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */\r
+ (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, slots * 4);\r
+ (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[0], data + slots, (count - slots) * 4);\r
+ }\r
+ else\r
+ {\r
+ (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, count * 4);\r
+ }\r
+}\r
+\r
+/*******************************************************************************\r
+ * prvTraceUserEventHelper1\r
+ *\r
+ * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on\r
+ * to the next helper function.\r
+ ******************************************************************************/\r
+static void prvTraceUserEventHelper1(UserEventChannel channel, traceLabel eventLabel, traceLabel formatLabel, va_list vl)\r
+{\r
+ uint32_t data[(3 + MAX_ARG_SIZE) / 4];\r
+ uint8_t byteOffset = 4; /* Need room for timestamp */\r
+ uint8_t noOfSlots;\r
+\r
+ if (channel == 0)\r
+ {\r
+ /* We are dealing with an unknown channel format pair */\r
+ byteOffset += 4; /* Also need room for channel and format */\r
+ ((uint16_t*)data)[2] = eventLabel;\r
+ ((uint16_t*)data)[3] = formatLabel;\r
+ }\r
+\r
+ noOfSlots = prvTraceUserEventFormat((char*)&(RecorderDataPtr->SymbolTable.symbytes[formatLabel+4]), vl, (uint8_t*)data, byteOffset);\r
+\r
+ prvTraceUserEventHelper2(channel, data, noOfSlots);\r
+}\r
+\r
+/*******************************************************************************\r
+ * prvTraceUserEventHelper2\r
+ *\r
+ * This function simply copies the data buffer to the actual user event buffer.\r
+ ******************************************************************************/\r
+static void prvTraceUserEventHelper2(UserEventChannel channel, uint32_t* data, uint32_t noOfSlots)\r
+{\r
+ static uint32_t old_timestamp = 0;\r
+ uint32_t old_nextSlotToWrite = 0;\r
+\r
+ TRACE_ASSERT(USER_EVENT_BUFFER_SIZE >= noOfSlots, "vTracePrintF: USER_EVENT_BUFFER_SIZE is too small to handle this event.", );\r
+\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ /* Store the timestamp */\r
+ vTracePortGetTimeStamp(data);\r
+\r
+ if (*data < old_timestamp)\r
+ {\r
+ RecorderDataPtr->userEventBuffer.wraparoundCounter++;\r
+ }\r
+\r
+ old_timestamp = *data;\r
+\r
+ /* Start by erasing any information in the channel buffer */\r
+ prvTraceClearChannelBuffer(noOfSlots);\r
+\r
+ prvTraceCopyToDataBuffer(data, noOfSlots); /* Will wrap around the data if necessary */\r
+\r
+ old_nextSlotToWrite = RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Save the index that we want to write the channel data at when we're done */\r
+ RecorderDataPtr->userEventBuffer.nextSlotToWrite = (RecorderDataPtr->userEventBuffer.nextSlotToWrite + noOfSlots) % USER_EVENT_BUFFER_SIZE; /* Make sure we never end up outside the buffer */\r
+\r
+ /* Write to the channel buffer to indicate that this user event is ready to be used */\r
+ if (channel != 0)\r
+ {\r
+ RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = channel;\r
+ }\r
+ else\r
+ {\r
+ /* 0xFF indicates that this is not a normal channel id */\r
+ RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (UserEventChannel)0xFF;\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
+}\r
+\r
+/*******************************************************************************\r
+ * xTraceRegisterChannelFormat\r
+ *\r
+ * Attempts to create a pair of the channel and format string.\r
+ *\r
+ * Note: This is only available if USE_SEPARATE_USER_EVENT_BUFFER is enabled in\r
+ * trcConfig.h\r
+ ******************************************************************************/\r
+UserEventChannel xTraceRegisterChannelFormat(traceLabel channel, traceLabel formatStr)\r
+{\r
+ uint8_t i;\r
+ UserEventChannel retVal = 0;\r
+\r
+ TRACE_ASSERT(formatStr != 0, "vTraceRegisterChannelFormat: formatStr == 0", (UserEventChannel)0);\r
+\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ for (i = 1; i <= CHANNEL_FORMAT_PAIRS; i++) /* Size of the channels buffer is CHANNEL_FORMAT_PAIRS + 1. Index 0 is unused. */\r
+ {\r
+ if(RecorderDataPtr->userEventBuffer.channels[i].name == 0 && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == 0)\r
+ {\r
+ /* Found empty slot */\r
+ RecorderDataPtr->userEventBuffer.channels[i].name = channel;\r
+ RecorderDataPtr->userEventBuffer.channels[i].defaultFormat = formatStr;\r
+ retVal = (UserEventChannel)i;\r
+ break;\r
+ }\r
+\r
+ if (RecorderDataPtr->userEventBuffer.channels[i].name == channel && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == formatStr)\r
+ {\r
+ /* Found a match */\r
+ retVal = (UserEventChannel)i;\r
+ break;\r
+ }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
+ return retVal;\r
+}\r
+\r
+/******************************************************************************\r
+ * vTraceChannelPrintF\r
+ *\r
+ * Slightly faster version of vTracePrintF() due to no lookups.\r
+ *\r
+ * Note: This is only available if USE_SEPARATE_USER_EVENT_BUFFER is enabled in\r
+ * trcConfig.h\r
+ *\r
+ ******************************************************************************/\r
+void vTraceChannelPrintF(UserEventChannel channelPair, ...)\r
+{\r
+#if (TRACE_SCHEDULING_ONLY == 0)\r
+ va_list vl;\r
+\r
+ va_start(vl, channelPair);\r
+ vTraceChannelPrintF_Helper(channelPair, vl);\r
+ va_end(vl);\r
+#endif /* TRACE_SCHEDULING_ONLY */\r
+}\r
+\r
+void vTraceChannelPrintF_Helper(UserEventChannel channelPair, va_list vl)\r
+{\r
+ traceLabel channel;\r
+ traceLabel formatStr;\r
+\r
+ TRACE_ASSERT(channelPair != 0, "vTraceChannelPrintF: channelPair == 0", );\r
+ TRACE_ASSERT(channelPair <= CHANNEL_FORMAT_PAIRS, "vTraceChannelPrintF: ", );\r
+\r
+ channel = RecorderDataPtr->userEventBuffer.channels[channelPair].name;\r
+ formatStr = RecorderDataPtr->userEventBuffer.channels[channelPair].defaultFormat;\r
+\r
+ prvTraceUserEventHelper1(channelPair, channel, formatStr, vl);\r
+}\r
+\r
+/******************************************************************************\r
+ * vTraceChannelUserEvent\r
+ *\r
+ * Slightly faster version of vTraceUserEvent() due to no lookups.\r
+ ******************************************************************************/\r
+void vTraceChannelUserEvent(UserEventChannel channelPair)\r
+{\r
+#if (TRACE_SCHEDULING_ONLY == 0)\r
+ uint32_t data[(3 + MAX_ARG_SIZE) / 4];\r
+\r
+ TRACE_ASSERT(channelPair != 0, "vTraceChannelPrintF: channelPair == 0", );\r
+ TRACE_ASSERT(channelPair <= CHANNEL_FORMAT_PAIRS, "vTraceChannelPrintF: ", );\r
+\r
+ prvTraceUserEventHelper2(channelPair, data, 1); /* Only need one slot for timestamp */\r
+#endif /* TRACE_SCHEDULING_ONLY */\r
+}\r
+#endif /* USE_SEPARATE_USER_EVENT_BUFFER == 1 */\r
+\r
+/******************************************************************************\r
+ * vTracePrintF\r
+ *\r
* Advanced user events (Professional Edition only)\r
*\r
* Generates User Event with formatted text and data, similar to a "printf".\r
- * It is very fast compared to a normal "printf" since this function only \r
+ * It is very fast compared to a normal "printf" since this function only\r
* stores the arguments. The actual formatting is done\r
- * on the host PC when the trace is displayed in the viewer tool. \r
+ * on the host PC when the trace is displayed in the viewer tool.\r
*\r
* User Event labels are created using xTraceOpenLabel.\r
* Example:\r
*\r
- * traceLabel adc_uechannel = xTraceOpenLabel("ADC User Events");\r
- * ...\r
- * vTracePrint(adc_uechannel, \r
- * "ADC channel %d: %lf volts", \r
- * ch, (double)adc_reading/(double)scale);\r
+ * traceLabel adc_uechannel = xTraceOpenLabel("ADC User Events");\r
+ * ...\r
+ * vTracePrint(adc_uechannel,\r
+ * "ADC channel %d: %lf volts",\r
+ * ch, (double)adc_reading/(double)scale);\r
*\r
* This can be combined into one line, if desired, but this is slower:\r
*\r
- * vTracePrint(xTraceOpenLabel("ADC User Events"), \r
- * "ADC channel %d: %lf volts", \r
- * ch, (double)adc_reading/(double)scale);\r
+ * vTracePrint(xTraceOpenLabel("ADC User Events"),\r
+ * "ADC channel %d: %lf volts",\r
+ * ch, (double)adc_reading/(double)scale);\r
*\r
* Calling xTraceOpenLabel multiple times will not create duplicate entries, but\r
- * it is of course faster to just do it once, and then keep the handle for later \r
- * use. If you don´t have any data arguments, only a text label/string, it is \r
+ * it is of course faster to just do it once, and then keep the handle for later\r
+ * use. If you don´t have any data arguments, only a text label/string, it is\r
* better to use vTraceUserEvent - it is faster.\r
*\r
* Format specifiers supported:\r
- * %d - 32 bit signed integer\r
- * %u - 32 bit unsigned integer\r
- * %f - 32 bit float\r
- * %s - string (is copied to the recorder symbol table)\r
- * %hd - 16 bit signed integer\r
- * %hu - 16 bit unsigned integer\r
- * %bd - 8 bit signed integer\r
- * %bu - 8 bit unsigned integer\r
- * %lf - double-precision float\r
- * \r
+ * %d - 32 bit signed integer\r
+ * %u - 32 bit unsigned integer\r
+ * %f - 32 bit float\r
+ * %s - string (is copied to the recorder symbol table)\r
+ * %hd - 16 bit signed integer\r
+ * %hu - 16 bit unsigned integer\r
+ * %bd - 8 bit signed integer\r
+ * %bu - 8 bit unsigned integer\r
+ * %lf - double-precision float (Note! See below...)\r
+ *\r
* Up to 15 data arguments are allowed, with a total size of maximum 32 byte.\r
* In case this is exceeded, the user event is changed into an error message.\r
- * \r
- * The data is stored in trace buffer, and is packed to allow storing multiple \r
+ *\r
+ * The data is stored in trace buffer, and is packed to allow storing multiple\r
* smaller data entries in the same 4-byte record, e.g., four 8-bit values.\r
- * A string requires two bytes, as the symbol table is limited to 64K. Storing a \r
- * double (%lf) uses two records, so this is quite costly. Use float (%f) unless\r
- * the higher precision is really necessary.\r
+ * A string requires two bytes, as the symbol table is limited to 64K. Storing\r
+ * a double (%lf) uses two records, so this is quite costly. Use float (%f)\r
+ * unless the higher precision is really necessary.\r
+ *\r
+ * Note that the double-precision float (%lf) assumes a 64 bit double\r
+ * representation. This does not seem to be the case on e.g. PIC24 and PIC32.\r
+ * Before using a %lf argument on a 16-bit MCU, please verify that\r
+ * "sizeof(double)" actually gives 8 as expected. If not, use %f instead.\r
******************************************************************************/\r
+\r
void vTracePrintF(traceLabel eventLabel, const char* formatStr, ...)\r
{\r
- UserEvent* ue1;\r
- va_list vl;\r
- uint8_t argCounter = 0;\r
- uint8_t i = 0;\r
- uint8_t nofEventEntries = 0;\r
- uint16_t formatStrIndex = 0; \r
-\r
- /**************************************************************************\r
- * The array tempDataBuffer is a local buffer used in a two-phase commit of \r
- * the event data, since a vTracePrintF may span over multiple slots in the \r
- * buffer.\r
- * This buffer can be made larger, of course, but remember the risk for \r
- * stack overflow. Note: This should be a LOCAL buffer, must not be made \r
- * global. That would cause data corruption when two calls to vTracePrintF\r
- * from different tasks overlaps (interrupts are only disabled in a small \r
- * part of this function, otherwise enabled)\r
- ***************************************************************************/\r
- \r
- uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4]; \r
- \r
- \r
- if ((inExcludedTask == 0) &&\r
- (nISRactive == 0) &&\r
- (RecorderDataPtr->recorderActive == 1) &&\r
- (handle_of_last_logged_task > 0))\r
- { \r
- /* First, write the "primary" user event entry in the local buffer, but \r
- let the event type be "EVENT_BEING_WRITTEN" for now...*/\r
-\r
- ue1 = (UserEvent*)(&tempDataBuffer[0]); \r
- ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */\r
- \r
- i = 4;\r
- formatStrIndex = 0;\r
- va_start(vl, formatStr); /* Begin reading the arguments list */\r
-\r
- while (formatStr[formatStrIndex] != '\0')\r
- {\r
- if (formatStr[formatStrIndex] == '%')\r
- {\r
- argCounter++;\r
-\r
- if (argCounter > 15)\r
- {\r
- vTraceError("vTracePrintF - Too many arguments, max 15 allowed!");\r
- va_end(vl); \r
- formatStr = "[vTracePrintF error] Too many arguments, max 15 allowed!";\r
- i = 4;\r
- break; \r
- }\r
+#if (TRACE_SCHEDULING_ONLY == 0)\r
+ va_list vl;\r
\r
-/*******************************************************************************\r
- * These below code writes raw data (primitive datatypes) in the event buffer, \r
- * instead of the normal event structs (where byte 0 is event type).\r
- * These data entries must never be interpreted as real event data, as the type\r
- * field would be misleading since used for payload data. \r
- * \r
- * The correctness of this encoding depends on two mechanisms:\r
- * \r
- * 1. An initial USER_EVENT, which type code tells the number of 32-bit data \r
- * entires that follows. (code - USER_EVENT = number of data entries). \r
- * Note that a data entry corresponds to the slots that normally corresponds to \r
- * one (1) event, i.e., 32 bits. vTracePrintF may encode several pieces of data \r
- * in one data entry, e.g., two 16-bit values or four 8-bit values, one 16-bit\r
- * value followes by two 8-bit values, etc.\r
- *\r
- * 2. A two-phase commit procedure, where the USER_EVENT and data entries are \r
- * written to a local buffer at first, and when all checks are OK then copied to\r
- * the main event buffer using a fast memcpy. The event code is finalized as the\r
- * very last step. Before that that step, the event code indicates an unfinished\r
- * event, which causes it to be ignored and stop the loading of the file (since \r
- * an unfinished event is the last event in the trace).\r
-*******************************************************************************/\r
- formatStrIndex++;\r
- switch (formatStr[formatStrIndex])\r
- {\r
- case 'd': i = writeInt32((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint32_t)va_arg(vl, uint32_t)); \r
- break;\r
- case 'u': i = writeInt32((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint32_t)va_arg(vl, uint32_t)); \r
- break;\r
- case 's': i = writeInt16((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint16_t)xTraceOpenLabel((char*)va_arg(vl, char*))); \r
- break;\r
+ va_start(vl, formatStr);\r
+ vTracePrintF_Helper(eventLabel, formatStr, vl);\r
+ va_end(vl);\r
+#endif /* TRACE_SCHEDULING_ONLY */\r
+}\r
\r
-#if (INCLUDE_FLOAT_SUPPORT)\r
- /* Yes, "double" as type also in the float \r
- case. This since "float" is promoted into "double" \r
- by the va_arg stuff. */\r
- case 'f': i = writeFloat((uint8_t*)tempDataBuffer, \r
- i, \r
- (float)va_arg(vl, double)); \r
- break; \r
-#else\r
-/* No support for floats, but attempt to store a float user event\r
-avoid a possible crash due to float reference. Instead store the \r
-data on uint_32 format (will not be displayed anyway). This is just\r
-to keep va_arg and i consistent. */\r
-\r
- case 'f': i = writeInt32((uint8_t*)tempDataBuffer,\r
- i, \r
- (uint32_t)va_arg(vl, double)); \r
-#endif\r
- case 'l':\r
- formatStrIndex++;\r
- switch (formatStr[formatStrIndex])\r
- {\r
-#if (INCLUDE_FLOAT_SUPPORT)\r
- case 'f': i = writeDouble((uint8_t*)tempDataBuffer, \r
- i, \r
- (double)va_arg(vl, double)); \r
- break;\r
-#else\r
-/* No support for floats, but attempt to store a float user event\r
-avoid a possible crash due to float reference. Instead store the \r
-data on uint_32 format (will not be displayed anyway). This is just\r
-to keep va_arg and i consistent. */\r
- case 'f': i = writeInt32((uint8_t*)tempDataBuffer, /* In this case, the value will not be shown anyway */\r
- i, \r
- (uint32_t)va_arg(vl, double)); \r
- i = writeInt32((uint8_t*)tempDataBuffer, /* Do it twice, to write in total 8 bytes */\r
- i, \r
- (uint32_t)va_arg(vl, double)); \r
-#endif\r
+void vTracePrintF_Helper(traceLabel eventLabel, const char* formatStr, va_list vl)\r
+{\r
+#if (USE_SEPARATE_USER_EVENT_BUFFER == 0)\r
+ uint32_t noOfSlots;\r
+ UserEvent* ue1;\r
+ uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
+\r
+ /**************************************************************************\r
+ * The array tempDataBuffer is a local buffer used in a two-phase commit of\r
+ * the event data, since a vTracePrintF may span over multiple slots in the\r
+ * buffer.\r
+ * This buffer can be made larger, of course, but remember the risk for\r
+ * stack overflow. Note: This should be a LOCAL buffer, must not be made\r
+ * global. That would cause data corruption when two calls to vTracePrintF\r
+ * from different tasks overlaps (interrupts are only disabled in a small\r
+ * part of this function, otherwise enabled)\r
+ ***************************************************************************/\r
+\r
+ TRACE_ASSERT(formatStr != NULL, "vTracePrintF: formatStr == NULL", );\r
+\r
+ trcCRITICAL_SECTION_BEGIN();\r
+\r
+ if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
+ {\r
+ /* First, write the "primary" user event entry in the local buffer, but\r
+ let the event type be "EVENT_BEING_WRITTEN" for now...*/\r
\r
- }\r
- break;\r
- case 'h':\r
- formatStrIndex++;\r
- switch (formatStr[formatStrIndex])\r
- {\r
- case 'd': i = writeInt16((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint16_t)va_arg(vl, uint32_t)); \r
- break;\r
- case 'u': i = writeInt16((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint16_t)va_arg(vl, uint32_t)); \r
- break;\r
- }\r
- break;\r
- case 'b':\r
- formatStrIndex++;\r
- switch (formatStr[formatStrIndex])\r
- {\r
- case 'd': i = writeInt8((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint8_t)va_arg(vl, uint32_t)); \r
- break;\r
- case 'u': i = writeInt8((uint8_t*)tempDataBuffer, \r
- i, \r
- (uint8_t)va_arg(vl, uint32_t)); \r
- break;\r
- }\r
- break;\r
- }\r
- } \r
- formatStrIndex++; \r
- if (i == 255)\r
- {\r
- va_end(vl);\r
- //vTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!");\r
- formatStr = "[vTracePrintF error] Too large arguments, max 32 byte allowed!";\r
- i = 4;\r
- break;\r
- }\r
- }\r
-\r
- va_end(vl);\r
- \r
- /* Store the format string, with a reference to the channel symbol */\r
- ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel); \r
-\r
- trcCRITICAL_SECTION_BEGIN(); \r
-\r
- ue1->dts = (uint8_t)prvTraceGetDTS(0xFF);\r
- if (! RecorderDataPtr->recorderActive)\r
- {\r
-\r
- /* Abort, since an XTS event (created by prvTraceGetDTS) filled the \r
- buffer, and the recorder stopped since not circular buffer. */\r
- trcCRITICAL_SECTION_END();\r
- \r
- return;\r
- }\r
- \r
- nofEventEntries = (i+3)/4;\r
-\r
- /* If the data does not fit in the remaining main buffer, wrap around to \r
- 0 if allowed, otherwise stop the recorder and quit). */\r
- if (RecorderDataPtr->nextFreeIndex + nofEventEntries > RecorderDataPtr->maxEvents)\r
- {\r
-#if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)\r
- (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4], \r
- 0, \r
- (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4);\r
- RecorderDataPtr->nextFreeIndex = 0;\r
- RecorderDataPtr->bufferIsFull = 1;\r
-#else\r
- /* Abort and stop recorder, since the event data will not fit in the\r
- buffer and not circular buffer in this case... */\r
- trcCRITICAL_SECTION_END();\r
- vTraceStop();\r
+ ue1 = (UserEvent*)(&tempDataBuffer[0]);\r
\r
- \r
- return;\r
-#endif\r
- }\r
- \r
-#if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)\r
- /* Check that the buffer to be overwritten does not contain any user \r
- events that would be partially overwritten. If so, they must be "killed"\r
- by replacing the user event and following data with NULL events (i.e., \r
- using a memset to zero).*/\r
- prvCheckDataToBeOverwrittenForMultiEntryUserEvents(nofEventEntries);\r
-#endif\r
- /* Copy the local buffer to the main buffer */\r
- (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4], \r
- tempDataBuffer, \r
- i);\r
-\r
- /* Update the event type, i.e., number of data entries following the \r
- main USER_EVENT entry (Note: important that this is after the memcpy, \r
- but within the critical section!)*/\r
- RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4] = \r
- (uint8_t) USER_EVENT + nofEventEntries - 1; \r
- \r
- /* Update the main buffer event index (already checked that it fits in \r
- the buffer, so no need to check for wrapping)*/\r
- \r
- RecorderDataPtr->nextFreeIndex += nofEventEntries;\r
- RecorderDataPtr->numEvents += nofEventEntries;\r
- \r
- if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE)\r
- {\r
- \r
-#if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)\r
- RecorderDataPtr->nextFreeIndex = 0;\r
- RecorderDataPtr->bufferIsFull = 1;\r
-#else\r
- vTraceStop();\r
+ ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */\r
+\r
+ noOfSlots = prvTraceUserEventFormat(formatStr, vl, (uint8_t*)tempDataBuffer, 4);\r
+\r
+ /* Store the format string, with a reference to the channel symbol */\r
+ ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel);\r
+\r
+ ue1->dts = (uint8_t)prvTraceGetDTS(0xFF);\r
+\r
+ /* prvTraceGetDTS might stop the recorder in some cases... */\r
+ if (RecorderDataPtr->recorderActive)\r
+ {\r
+\r
+ /* If the data does not fit in the remaining main buffer, wrap around to\r
+ 0 if allowed, otherwise stop the recorder and quit). */\r
+ if (RecorderDataPtr->nextFreeIndex + noOfSlots > RecorderDataPtr->maxEvents)\r
+ {\r
+ #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER)\r
+ (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],\r
+ 0,\r
+ (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4);\r
+ RecorderDataPtr->nextFreeIndex = 0;\r
+ RecorderDataPtr->bufferIsFull = 1;\r
+ #else\r
+\r
+ /* Stop recorder, since the event data will not fit in the\r
+ buffer and not circular buffer in this case... */\r
+ vTraceStop();\r
+ #endif\r
+ }\r
+\r
+ /* Check if recorder has been stopped (i.e., vTraceStop above) */\r
+ if (RecorderDataPtr->recorderActive)\r
+ {\r
+ /* Check that the buffer to be overwritten does not contain any user\r
+ events that would be partially overwritten. If so, they must be "killed"\r
+ by replacing the user event and following data with NULL events (i.e.,\r
+ using a memset to zero).*/\r
+ #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER)\r
+ prvCheckDataToBeOverwrittenForMultiEntryEvents((uint8_t)noOfSlots);\r
+ #endif\r
+ /* Copy the local buffer to the main buffer */\r
+ (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],\r
+ tempDataBuffer,\r
+ noOfSlots * 4);\r
+\r
+ /* Update the event type, i.e., number of data entries following the\r
+ main USER_EVENT entry (Note: important that this is after the memcpy,\r
+ but within the critical section!)*/\r
+ RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4] =\r
+ (uint8_t) ( USER_EVENT + noOfSlots - 1 );\r
+\r
+ /* Update the main buffer event index (already checked that it fits in\r
+ the buffer, so no need to check for wrapping)*/\r
+\r
+ RecorderDataPtr->nextFreeIndex += noOfSlots;\r
+ RecorderDataPtr->numEvents += noOfSlots;\r
+\r
+ if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE)\r
+ {\r
+ #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER)\r
+ /* We have reached the end, but this is a ring buffer. Start from the beginning again. */\r
+ RecorderDataPtr->bufferIsFull = 1;\r
+ RecorderDataPtr->nextFreeIndex = 0;\r
+ #else\r
+ /* We have reached the end so we stop. */\r
+ vTraceStop();\r
+ #endif\r
+ }\r
+ }\r
+\r
+ #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER)\r
+ /* Make sure the next entry is cleared correctly */\r
+ prvCheckDataToBeOverwrittenForMultiEntryEvents(1);\r
+ #endif\r
+\r
+ }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
+\r
+#elif (USE_SEPARATE_USER_EVENT_BUFFER == 1)\r
+ /* Use the separate user event buffer */\r
+ traceLabel formatLabel;\r
+ UserEventChannel channel;\r
+\r
+ if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
+ {\r
+ formatLabel = xTraceOpenLabel(formatStr);\r
+\r
+ channel = xTraceRegisterChannelFormat(eventLabel, formatLabel);\r
+\r
+ prvTraceUserEventHelper1(channel, eventLabel, formatLabel, vl);\r
+ }\r
#endif\r
- }\r
+}\r
+\r
+/******************************************************************************\r
+ * vTraceUserEvent\r
+ *\r
+ * Basic user event (Standard and Professional Edition only)\r
+ *\r
+ * Generates a User Event with a text label. The label is created/looked up\r
+ * in the symbol table using xTraceOpenLabel.\r
+ ******************************************************************************/\r
+void vTraceUserEvent(traceLabel eventLabel)\r
+{\r
+#if (TRACE_SCHEDULING_ONLY == 0)\r
+#if (USE_SEPARATE_USER_EVENT_BUFFER == 0)\r
+ UserEvent* ue;\r
+ uint8_t dts1;\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
+\r
+ TRACE_ASSERT(eventLabel > 0, "vTraceUserEvent: Invalid value for eventLabel", );\r
+\r
+ trcCRITICAL_SECTION_BEGIN();\r
+ if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
+ {\r
+ dts1 = (uint8_t)prvTraceGetDTS(0xFF);\r
+ ue = (UserEvent*) xTraceNextFreeEventBufferSlot();\r
+ if (ue != NULL)\r
+ {\r
+ ue->dts = dts1;\r
+ ue->type = USER_EVENT;\r
+ ue->payload = eventLabel;\r
+ prvTraceUpdateCounters();\r
+ }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
+\r
+#elif (USE_SEPARATE_USER_EVENT_BUFFER == 1)\r
+ UserEventChannel channel;\r
+ uint32_t noOfSlots = 1;\r
+ uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];\r
+ if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
+ {\r
+ channel = xTraceRegisterChannelFormat(0, eventLabel);\r
\r
- trcCRITICAL_SECTION_END();\r
- } \r
+ if (channel == 0)\r
+ {\r
+ /* We are dealing with an unknown channel format pair */\r
+ noOfSlots++; /* Also need room for channel and format */\r
+ ((uint16_t*)tempDataBuffer)[2] = 0;\r
+ ((uint16_t*)tempDataBuffer)[3] = eventLabel;\r
+ }\r
+\r
+ prvTraceUserEventHelper2(channel, tempDataBuffer, noOfSlots);\r
+ }\r
+#endif\r
+#endif /* TRACE_SCHEDULING_ONLY */\r
}\r
- \r
+\r
/*******************************************************************************\r
* xTraceOpenLabel\r
- * \r
+ *\r
* Creates user event labels for user event channels or for individual events.\r
* User events can be used to log application events and data for display in\r
* the visualization tool. A user event is identified by a label, i.e., a string,\r
* which is stored in the recorder's symbol table.\r
* When logging a user event, a numeric handle (reference) to this string is\r
- * used to identify the event. This is obtained by calling \r
- * \r
- * xTraceOpenLabel()\r
+ * used to identify the event. This is obtained by calling\r
+ *\r
+ * xTraceOpenLabel()\r
*\r
* which adds the string to the symbol table (if not already present)\r
* and returns the corresponding handle.\r
* 1. The handle is looked up every time, when storing the user event.\r
*\r
* Example:\r
- * vTraceUserEvent(xTraceOpenLabel("MyUserEvent"));\r
+ * vTraceUserEvent(xTraceOpenLabel("MyUserEvent"));\r
*\r
* 2. The label is registered just once, with the handle stored in an\r
- * application variable - much like using a file handle.\r
+ * application variable - much like using a file handle.\r
*\r
* Example:\r
- * myEventHandle = xTraceOpenLabel("MyUserEvent");\r
- * ...\r
- * vTraceUserEvent(myEventHandle);\r
+ * myEventHandle = xTraceOpenLabel("MyUserEvent");\r
+ * ...\r
+ * vTraceUserEvent(myEventHandle);\r
*\r
- * The second option is faster since no lookup is required on each event, and \r
+ * The second option is faster since no lookup is required on each event, and\r
* therefore recommended for user events that are frequently\r
* executed and/or located in time-critical code. The lookup operation is\r
* however fairly fast due to the design of the symbol table.\r
******************************************************************************/\r
traceLabel xTraceOpenLabel(const char* label)\r
{\r
- return prvTraceOpenSymbol(label, 0);\r
+ TRACE_ASSERT(label != NULL, "xTraceOpenLabel: label == NULL", (traceLabel)0);\r
+\r
+ return prvTraceOpenSymbol(label, 0);\r
}\r
-#endif\r
+\r
#endif\r
\r
+#endif\r