/*******************************************************************************\r
- * Tracealyzer v2.5.0 Recorder Library\r
+ * Tracealyzer v2.7.0 Recorder Library\r
* Percepio AB, www.percepio.com\r
*\r
* trcUser.c\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
- * Copyright Percepio AB, 2013.\r
+ * Tabs are used for indent in this file (1 tab = 4 spaces)\r
+ *\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
\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 char* traceErrorMessage;\r
\r
-/*** private functions *******************************************************/\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
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
\r
+static void prvTraceTaskInstanceFinish(int8_t direct);\r
\r
\r
/*******************************************************************************\r
}\r
#endif\r
\r
+/*******************************************************************************\r
+ * vTraceSetStopHook\r
+ *\r
+ * Sets a function to be called when the recorder is stopped.\r
+ ******************************************************************************/\r
+void vTraceSetStopHook(TRACE_STOP_HOOK stopHookFunction)\r
+{\r
+ vTraceStopHookPtr = stopHookFunction;\r
+}\r
+\r
/*******************************************************************************\r
* vTraceClear\r
*\r
******************************************************************************/\r
void vTraceClear(void)\r
{\r
- trcCRITICAL_SECTION_BEGIN();\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\r
+ trcCRITICAL_SECTION_BEGIN();\r
\r
- RecorderDataPtr->absTimeLastEvent = 0;\r
- RecorderDataPtr->nextFreeIndex = 0;\r
- RecorderDataPtr->numEvents = 0;\r
- RecorderDataPtr->bufferIsFull = 0;\r
+ RecorderDataPtr->absTimeLastEventSecond = 0;\r
\r
- trcCRITICAL_SECTION_END();\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
uint32_t uiTraceStart(void)\r
{\r
- objectHandleType handle = 0;\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
- if (traceErrorMessage == NULL)\r
- {\r
- trcCRITICAL_SECTION_BEGIN();\r
- RecorderDataPtr->recorderActive = 1;\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
vTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0);\r
}\r
\r
- vTraceStoreTaskswitch(handle); /* Register the currently running task */\r
- trcCRITICAL_SECTION_END();\r
- }\r
+ vTraceStoreTaskswitch(handle); /* Register the currently running task */\r
+ trcCRITICAL_SECTION_END();\r
+ }\r
\r
- return RecorderDataPtr->recorderActive;\r
+ return RecorderDataPtr->recorderActive;\r
}\r
\r
/*******************************************************************************\r
******************************************************************************/\r
void vTraceStart(void)\r
{\r
- (void)uiTraceStart();\r
+ (void)uiTraceStart();\r
}\r
\r
/*******************************************************************************\r
******************************************************************************/\r
void vTraceStop(void)\r
{\r
- RecorderDataPtr->recorderActive = 0;\r
- \r
+ RecorderDataPtr->recorderActive = 0;\r
+\r
if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0)\r
{\r
- (*vTraceStopHookPtr)(); /* Call an application level call back function. */\r
+ (*vTraceStopHookPtr)(); /* An application call-back function. */\r
}\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
+* 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
+ traceErrorMessage = NULL;\r
RecorderDataPtr->internalErrorOccured = 0;\r
}\r
\r
******************************************************************************/\r
void* vTraceGetTraceBuffer(void)\r
{\r
- return RecorderDataPtr;\r
+ return RecorderDataPtr;\r
}\r
\r
/*******************************************************************************\r
******************************************************************************/\r
uint32_t uiTraceGetTraceBufferSize(void)\r
{\r
- return sizeof(RecorderDataType);\r
+ return sizeof(RecorderDataType);\r
+}\r
+\r
+/******************************************************************************\r
+ * prvTraceTaskInstanceFinish.\r
+ *\r
+ * Private common function for the vTraceTaskInstanceFinishXXX functions.\r
+ * \r
+ *****************************************************************************/\r
+void prvTraceTaskInstanceFinish(int8_t direct)\r
+{\r
+ TaskInstanceStatusEvent* tis;\r
+ uint8_t dts45;\r
+\r
+ TRACE_SR_ALLOC_CRITICAL_SECTION();\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
+ tis->dts = dts45;\r
+ prvTraceUpdateCounters();\r
+ }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
+}\r
+\r
+/******************************************************************************\r
+ * vTraceTaskInstanceFinish(void)\r
+ *\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
+ * See also USE_IMPLICIT_IFE_RULES in trcConfig.h\r
+ *\r
+ * Example:\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
+ prvTraceTaskInstanceFinish(0);\r
}\r
\r
/******************************************************************************\r
- * vTraceTaskInstanceIsFinished\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
- * 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 \r
- * context-switch. This function should be called right before the API function \r
- * call considered to be the Instance Finish Event.\r
*****************************************************************************/\r
-void vTraceTaskInstanceIsFinished()\r
+void vTraceTaskInstanceFinishDirect(void)\r
{\r
- if (handle_of_last_logged_task)\r
- {\r
- TRACE_PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle_of_last_logged_task) = 0;\r
- }\r
+ prvTraceTaskInstanceFinish(1);\r
}\r
\r
/*******************************************************************************\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
+ * #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
{\r
TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceSetISRProperties: Invalid value for handle", );\r
TRACE_ASSERT(name != NULL, "vTraceSetISRProperties: name == NULL", );\r
- TRACE_ASSERT(priority >= 0, "vTraceSetISRProperties: Invalid value for priority", );\r
\r
- vTraceSetObjectName(TRACE_CLASS_ISR, handle, name);\r
- vTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority);\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
* 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().\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
- TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid value for handle", );\r
-\r
- if (recorder_busy)\r
- {\r
- vTraceError("Illegal call to vTraceStoreISRBegin, recorder busy!");\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
-static int tailchain_irq_pending(void);\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
-/*******************************************************************************\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
-static 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 (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
+ * 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().\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 vTraceStoreISREnd, recorder busy!");\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(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
TRACE_ASSERT(buffer != NULL, "writeInt8: buffer == NULL", 0);\r
\r
- if (i >= MAX_ARG_SIZE)\r
- {\r
- return 255;\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
{\r
TRACE_ASSERT(buffer != NULL, "writeInt16: buffer == NULL", 0);\r
\r
- /* Align to multiple of 2 */\r
- while ((i % 2) != 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
+ ((uint8_t*)buffer)[i] = 0;\r
+ i++;\r
+ }\r
\r
- if (i + 2 > MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- }\r
+ if (i + 2 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
\r
- ((uint16_t*)buffer)[i/2] = value;\r
+ ((uint16_t*)buffer)[i/2] = value;\r
\r
- return i + 2;\r
+ return i + 2;\r
}\r
\r
/*** Locally used in vTracePrintF ***/\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
+ /* 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
+ if (i + 4 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
\r
- ((uint32_t*)buffer)[i/4] = value;\r
+ ((uint32_t*)buffer)[i/4] = value;\r
\r
- return i + 4;\r
+ return i + 4;\r
}\r
\r
#if (INCLUDE_FLOAT_SUPPORT)\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
+ /* 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
+ if (i + 4 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
\r
- ((float*)buffer)[i/4] = value;\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;\r
+ uint32_t * src = (uint32_t*)&value;\r
+\r
TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0);\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
+ /* 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
+ ((uint8_t*)buffer)[i] = 0;\r
+ i++;\r
+ }\r
+\r
+ if (i + 8 > MAX_ARG_SIZE)\r
+ {\r
+ return 255;\r
+ }\r
\r
- if (i + 8 > MAX_ARG_SIZE)\r
- {\r
- return 255;\r
- }\r
+ dest = &(((uint32_t *)buffer)[i/4]);\r
\r
- dest[i/4+0] = src[0];\r
- dest[i/4+1] = src[1];\r
+ dest[0] = src[0];\r
+ dest[1] = src[1];\r
\r
- return i + 8;\r
+ return i + 8;\r
}\r
\r
#endif\r
uint8_t i = byteOffset;\r
\r
while (formatStr[formatStrIndex] != '\0')\r
- {\r
- if (formatStr[formatStrIndex] == '%')\r
- {\r
- argCounter++;\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
+ 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
+ * 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
switch (formatStr[formatStrIndex])\r
{\r
- case 'd': i = writeInt32(buffer,\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
+ 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
+ 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
+ /* 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
+ /* 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
+ 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
+ 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
+ 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
- i = writeInt32(buffer, /* Do it twice, to write in total 8 bytes */\r
- i,\r
- (uint32_t)va_arg(vl, double));\r
- break;\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
- case 'u': i = writeInt8(buffer,\r
- i,\r
- (uint8_t)va_arg(vl, uint32_t));\r
- break;\r
- }\r
- break;\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
+ }\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
{\r
uint32_t slots;\r
\r
- TRACE_ASSERT(USER_EVENT_BUFFER_SIZE >= count, "prvTraceClearChannelBuffer: USER_EVENT_BUFFER_SIZE is too small to handle this event.", );\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
static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count)\r
{\r
- TRACE_ASSERT(data != NULL, "prvTraceCopyToDataBuffer: data == NULL.", );\r
- TRACE_ASSERT(count <= USER_EVENT_BUFFER_SIZE, "prvTraceCopyToDataBuffer: USER_EVENT_BUFFER_SIZE is too small to handle this event.", );\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
/*******************************************************************************\r
* prvTraceUserEventHelper1\r
*\r
- * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on to the next helper function.\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
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
\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
- RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (UserEventChannel)0xFF; /* 0xFF indicates that this is not a normal channel id */\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
* 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
+ * 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
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
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
+/******************************************************************************\r
* vTracePrintF\r
*\r
* Advanced user events (Professional Edition only)\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
* 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
+ * %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
* 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
+#if (TRACE_SCHEDULING_ONLY == 0)\r
va_list vl;\r
\r
va_start(vl, formatStr);\r
vTracePrintF_Helper(eventLabel, formatStr, vl);\r
va_end(vl);\r
+#endif /* TRACE_SCHEDULING_ONLY */\r
}\r
\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
-\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
+ 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
- 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
- ue1 = (UserEvent*)(&tempDataBuffer[0]);\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
- trcCRITICAL_SECTION_BEGIN();\r
-\r
- ue1->dts = (uint8_t)prvTraceGetDTS(0xFF);\r
- if (! RecorderDataPtr->recorderActive)\r
- {\r
+ trcCRITICAL_SECTION_BEGIN();\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
+ 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
- return;\r
- }\r
+ ue1 = (UserEvent*)(&tempDataBuffer[0]);\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
- /* 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->type = EVENT_BEING_WRITTEN; /* Update this as the last step */\r
\r
+ noOfSlots = prvTraceUserEventFormat(formatStr, vl, (uint8_t*)tempDataBuffer, 4);\r
\r
- return;\r
-#endif\r
- }\r
-\r
-#if (TRACE_RECORDER_STORE_MODE == TRACE_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
- 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
+ /* Store the format string, with a reference to the channel symbol */\r
+ ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel);\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
+ 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
- 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
+ /* 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
+ #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
-#ifdef STOP_AFTER_N_EVENTS\r
-#if (STOP_AFTER_N_EVENTS > -1)\r
- /* Check if we have reached the desired number of events */\r
- if (RecorderDataPtr->numEvents >= STOP_AFTER_N_EVENTS)\r
- {\r
- vTraceStop();\r
}\r
-#endif\r
-#endif\r
-\r
- trcCRITICAL_SECTION_END();\r
- }\r
+ }\r
+ trcCRITICAL_SECTION_END();\r
\r
#elif (USE_SEPARATE_USER_EVENT_BUFFER == 1)\r
/* Use the separate user event buffer */\r
UserEventChannel channel;\r
\r
if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
- {\r
- formatLabel = xTraceOpenLabel(formatStr);\r
+ {\r
+ formatLabel = xTraceOpenLabel(formatStr);\r
\r
- channel = xTraceRegisterChannelFormat(eventLabel, formatLabel);\r
+ channel = xTraceRegisterChannelFormat(eventLabel, formatLabel);\r
\r
prvTraceUserEventHelper1(channel, eventLabel, formatLabel, vl);\r
}\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
+ 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
- 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
+ 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
-\r
if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)\r
{\r
channel = xTraceRegisterChannelFormat(0, eventLabel);\r
prvTraceUserEventHelper2(channel, tempDataBuffer, noOfSlots);\r
}\r
#endif\r
+#endif /* TRACE_SCHEDULING_ONLY */\r
}\r
\r
/*******************************************************************************\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
+ * 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
* therefore recommended for user events that are frequently\r
{\r
TRACE_ASSERT(label != NULL, "xTraceOpenLabel: label == NULL", (traceLabel)0);\r
\r
- return prvTraceOpenSymbol(label, 0);\r
+ return prvTraceOpenSymbol(label, 0);\r
}\r
\r
#endif\r
\r
-#endif
\ No newline at end of file
+#endif\r