| /******************************************************************************* | |
| * Trace Recorder Library for Tracealyzer v4.1.5 | |
| * Percepio AB, www.percepio.com | |
| * | |
| * trcSnapshotRecorder.c | |
| * | |
| * The generic core of the trace recorder's snapshot mode. | |
| * | |
| * Terms of Use | |
| * This file is part of the trace recorder library (RECORDER), which is the | |
| * intellectual property of Percepio AB (PERCEPIO) and provided under a | |
| * license as follows. | |
| * The RECORDER may be used free of charge for the purpose of recording data | |
| * intended for analysis in PERCEPIO products. It may not be used or modified | |
| * for other purposes without explicit permission from PERCEPIO. | |
| * You may distribute the RECORDER in its original source code form, assuming | |
| * this text (terms of use, disclaimer, copyright notice) is unchanged. You are | |
| * allowed to distribute the RECORDER with minor modifications intended for | |
| * configuration or porting of the RECORDER, e.g., to allow using it on a | |
| * specific processor, processor family or with a specific communication | |
| * interface. Any such modifications should be documented directly below | |
| * this comment block. | |
| * | |
| * Disclaimer | |
| * The RECORDER is being delivered to you AS IS and PERCEPIO makes no warranty | |
| * as to its use or performance. PERCEPIO does not and cannot warrant the | |
| * performance or results you may obtain by using the RECORDER or documentation. | |
| * PERCEPIO make no warranties, express or implied, as to noninfringement of | |
| * third party rights, merchantability, or fitness for any particular purpose. | |
| * In no event will PERCEPIO, its technology partners, or distributors be liable | |
| * to you for any consequential, incidental or special damages, including any | |
| * lost profits or lost savings, even if a representative of PERCEPIO has been | |
| * advised of the possibility of such damages, or for any claim by any third | |
| * party. Some jurisdictions do not allow the exclusion or limitation of | |
| * incidental, consequential or special damages, or the exclusion of implied | |
| * warranties or limitations on how long an implied warranty may last, so the | |
| * above limitations may not apply to you. | |
| * | |
| * Tabs are used for indent in this file (1 tab = 4 spaces) | |
| * | |
| * Copyright Percepio AB, 2018. | |
| * www.percepio.com | |
| ******************************************************************************/ | |
| #include "trcRecorder.h" | |
| #if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT) | |
| #if (TRC_USE_TRACEALYZER_RECORDER == 1) | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include <stdint.h> | |
| #if ((TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR) || (TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR)) | |
| #error "CUSTOM timestamping mode is not (yet) supported in snapshot mode!" | |
| #endif | |
| /* DO NOT CHANGE */ | |
| #define TRACE_MINOR_VERSION 5 | |
| #if (TRC_CFG_INCLUDE_ISR_TRACING == 1) | |
| static traceHandle isrstack[TRC_CFG_MAX_ISR_NESTING]; | |
| int32_t isPendingContextSwitch = 0; | |
| #endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1) */ | |
| #if !defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1 | |
| static int readyEventsEnabled = 1; | |
| #endif /*!defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1*/ | |
| /******************************************************************************* | |
| * uiTraceTickCount | |
| * | |
| * This variable is should be updated by the Kernel tick interrupt. This does | |
| * not need to be modified when developing a new timer port. It is preferred to | |
| * keep any timer port changes in the HWTC macro definitions, which typically | |
| * give sufficient flexibility. | |
| ******************************************************************************/ | |
| uint32_t uiTraceTickCount = 0; | |
| uint32_t trace_disable_timestamp = 0; | |
| static uint32_t last_timestamp = 0; | |
| /* Flag that shows if inside a critical section of the recorder */ | |
| volatile int recorder_busy = 0; | |
| /* Holds the value set by vTraceSetFrequency */ | |
| uint32_t timestampFrequency = 0; | |
| /* The last error message of the recorder. NULL if no error message. */ | |
| const char* traceErrorMessage = NULL; | |
| int8_t nISRactive = 0; | |
| traceHandle handle_of_last_logged_task = 0; | |
| /* Called when the recorder is stopped, set by vTraceSetStopHook. */ | |
| TRACE_STOP_HOOK vTraceStopHookPtr = (TRACE_STOP_HOOK)0; | |
| uint16_t CurrentFilterMask = 0xFFFF; | |
| uint16_t CurrentFilterGroup = FilterGroup0; | |
| extern int8_t nISRactive; | |
| extern traceHandle handle_of_last_logged_task; | |
| /*************** Private Functions *******************************************/ | |
| static void prvStrncpy(char* dst, const char* src, uint32_t maxLength); | |
| static uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id); | |
| static void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength); | |
| static void* prvTraceNextFreeEventBufferSlot(void); | |
| static uint16_t prvTraceGetDTS(uint16_t param_maxDTS); | |
| static traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel); | |
| static void prvTraceUpdateCounters(void); | |
| void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size); | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| static void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nEntries); | |
| #endif | |
| static traceString prvTraceCreateSymbolTableEntry(const char* name, | |
| uint8_t crc6, | |
| uint8_t len, | |
| traceString channel); | |
| static traceString prvTraceLookupSymbolTableEntry(const char* name, | |
| uint8_t crc6, | |
| uint8_t len, | |
| traceString channel); | |
| #if (TRC_CFG_INCLUDE_ISR_TRACING == 0) | |
| /* ISR tracing is turned off */ | |
| void prvTraceIncreaseISRActive(void); | |
| void prvTraceDecreaseISRActive(void); | |
| #endif /*(TRC_CFG_INCLUDE_ISR_TRACING == 0)*/ | |
| #if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1) | |
| static uint8_t prvTraceGet8BitHandle(traceHandle handle); | |
| #else | |
| #define prvTraceGet8BitHandle(x) ((uint8_t)x) | |
| #endif | |
| #if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1) && (TRC_CFG_SCHEDULING_ONLY == 0) | |
| static uint32_t heapMemUsage = 0; | |
| #endif | |
| #if (TRC_CFG_SCHEDULING_ONLY == 0) | |
| static uint32_t prvTraceGetParam(uint32_t, uint32_t); | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceInitTraceData | |
| * | |
| * Allocates and initializes the recorder data structure, based on the constants | |
| * in trcConfig.h. This allows for allocating the data on the heap, instead of | |
| * using a static declaration. | |
| ******************************************************************************/ | |
| static void prvTraceInitTraceData(void); | |
| /******************************************************************************* | |
| * prvTracePortGetTimeStamp | |
| * | |
| * Returns the current time based on the HWTC macros which provide a hardware | |
| * isolation layer towards the hardware timer/counter. | |
| * | |
| * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue | |
| * or the trace recorder library. Typically you should not need to change | |
| * the code of prvTracePortGetTimeStamp if using the HWTC macros. | |
| * | |
| ******************************************************************************/ | |
| void prvTracePortGetTimeStamp(uint32_t *puiTimestamp); | |
| static void prvTraceTaskInstanceFinish(int8_t direct); | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| static void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl); | |
| #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| static void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl); | |
| static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl); | |
| static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots); | |
| #endif /*(TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)*/ | |
| #endif /* ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) */ | |
| /********* Public Functions **************************************************/ | |
| uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass); | |
| /******************************************************************************* | |
| * prvTraceError | |
| * | |
| * Called by various parts in the recorder. Stops the recorder and stores a | |
| * pointer to an error message, which is printed by the monitor task. | |
| ******************************************************************************/ | |
| void prvTraceError(const char* msg); | |
| /****************************************************************************** | |
| * vTraceEnable(int startOption) - snapshot mode | |
| * | |
| * Initializes and optionally starts the trace, depending on the start option. | |
| * To use the trace recorder, the startup must call vTraceEnable before any RTOS | |
| * calls are made (including "create" calls). Three start options are provided: | |
| * | |
| * TRC_START: Starts the tracing directly. In snapshot mode this allows for | |
| * starting the trace at any point in your code, assuming vTraceEnable(TRC_INIT) | |
| * has been called in the startup. | |
| * Can also be used for streaming without Tracealyzer control, e.g. to a local | |
| * flash file system (assuming such a "stream port", see trcStreamingPort.h). | |
| * | |
| * TRC_INIT: Initializes the trace recorder, but does not start the tracing. | |
| * In snapshot mode, this must be followed by a vTraceEnable(TRC_START) sometime | |
| * later. | |
| * | |
| * Usage examples, in snapshot mode: | |
| * | |
| * Snapshot trace, from startup: | |
| * <board init> | |
| * vTraceEnable(TRC_START); | |
| * <RTOS init> | |
| * | |
| * Snapshot trace, from a later point: | |
| * <board init> | |
| * vTraceEnable(TRC_INIT); | |
| * <RTOS init> | |
| * ... | |
| * vTraceEnable(TRC_START); // e.g., in task context, at some relevant event | |
| * | |
| * | |
| * Note: See other implementation of vTraceEnable in trcStreamingRecorder.c | |
| ******************************************************************************/ | |
| void vTraceEnable(int startOption) | |
| { | |
| prvTraceInitTraceData(); | |
| if (startOption == TRC_START) | |
| { | |
| vTraceStart(); | |
| } | |
| else if (startOption == TRC_START_AWAIT_HOST) | |
| { | |
| prvTraceError("vTraceEnable(TRC_START_AWAIT_HOST) not allowed in Snapshot mode"); | |
| } | |
| else if (startOption != TRC_INIT) | |
| { | |
| prvTraceError("Unexpected argument to vTraceEnable (snapshot mode)"); | |
| } | |
| } | |
| /******************************************************************************* | |
| * vTraceSetRecorderDataBuffer | |
| * | |
| * If custom allocation is used, this function must be called so the recorder | |
| * library knows where to save the trace data. | |
| ******************************************************************************/ | |
| #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM) | |
| void vTraceSetRecorderDataBuffer(void* pRecorderData) | |
| { | |
| TRACE_ASSERT(pRecorderData != NULL, "vTraceSetRecorderDataBuffer, pRecorderData == NULL", TRC_UNUSED); | |
| RecorderDataPtr = pRecorderData; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * vTraceSetStopHook | |
| * | |
| * Sets a function to be called when the recorder is stopped. This can be used | |
| * to save the trace to a file system, if available. This is only implemented | |
| * for snapshot mode. | |
| ******************************************************************************/ | |
| void vTraceSetStopHook(TRACE_STOP_HOOK stopHookFunction) | |
| { | |
| vTraceStopHookPtr = stopHookFunction; | |
| } | |
| /******************************************************************************* | |
| * vTraceClear | |
| * | |
| * Resets the recorder. Only necessary if a restart is desired - this is not | |
| * needed in the startup initialization. | |
| ******************************************************************************/ | |
| void vTraceClear(void) | |
| { | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| RecorderDataPtr->absTimeLastEventSecond = 0; | |
| RecorderDataPtr->absTimeLastEvent = 0; | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| RecorderDataPtr->numEvents = 0; | |
| RecorderDataPtr->bufferIsFull = 0; | |
| traceErrorMessage = NULL; | |
| RecorderDataPtr->internalErrorOccured = 0; | |
| (void)memset(RecorderDataPtr->eventData, 0, RecorderDataPtr->maxEvents * 4); | |
| handle_of_last_logged_task = 0; | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * uiTraceStart | |
| * | |
| * Starts the recorder. The recorder will not be started if an error has been | |
| * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h | |
| * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc). | |
| * | |
| * Returns 1 if the recorder was started successfully. | |
| * Returns 0 if the recorder start was prevented due to a previous internal | |
| * error. In that case, check xTraceGetLastError to get the error message. | |
| * Any error message is also presented when opening a trace file. | |
| * | |
| * This function is obsolete, but has been saved for backwards compatibility. | |
| * We recommend using vTraceEnable instead. | |
| ******************************************************************************/ | |
| uint32_t uiTraceStart(void) | |
| { | |
| traceHandle handle; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| handle = 0; | |
| if (RecorderDataPtr == NULL) | |
| { | |
| TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized. Use vTraceEnable() instead!", 0); | |
| return 0; | |
| } | |
| if (RecorderDataPtr->recorderActive == 1) | |
| return 1; /* Already running */ | |
| if (traceErrorMessage == NULL) | |
| { | |
| trcCRITICAL_SECTION_BEGIN(); | |
| RecorderDataPtr->recorderActive = 1; | |
| handle = TRACE_GET_TASK_NUMBER(TRACE_GET_CURRENT_TASK()); | |
| if (handle == 0) | |
| { | |
| /* This occurs if the scheduler is not yet started. | |
| This creates a dummy "(startup)" task entry internally in the | |
| recorder */ | |
| handle = prvTraceGetObjectHandle(TRACE_CLASS_TASK); | |
| prvTraceSetObjectName(TRACE_CLASS_TASK, handle, "(startup)"); | |
| prvTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0); | |
| } | |
| prvTraceStoreTaskswitch(handle); /* Register the currently running task */ | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| return RecorderDataPtr->recorderActive; | |
| } | |
| /******************************************************************************* | |
| * vTraceStart | |
| * | |
| * Starts the recorder. The recorder will not be started if an error has been | |
| * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h | |
| * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc). | |
| * | |
| * This function is obsolete, but has been saved for backwards compatibility. | |
| * We recommend using vTraceEnable instead. | |
| ******************************************************************************/ | |
| void vTraceStart(void) | |
| { | |
| (void)uiTraceStart(); | |
| } | |
| /******************************************************************************* | |
| * vTraceStop | |
| * | |
| * Stops the recorder. The recording can be resumed by calling vTraceStart. | |
| * This does not reset the recorder. Use vTraceClear if that is desired. | |
| ******************************************************************************/ | |
| void vTraceStop(void) | |
| { | |
| if (RecorderDataPtr != NULL) | |
| { | |
| RecorderDataPtr->recorderActive = 0; | |
| } | |
| if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0) | |
| { | |
| (*vTraceStopHookPtr)(); /* An application call-back function. */ | |
| } | |
| } | |
| /******************************************************************************* | |
| * xTraceIsRecordingEnabled | |
| * Returns true (1) if the recorder is enabled (i.e. is recording), otherwise 0. | |
| ******************************************************************************/ | |
| int xTraceIsRecordingEnabled(void) | |
| { | |
| if (RecorderDataPtr != NULL) | |
| { | |
| return (int)RecorderDataPtr->recorderActive; | |
| } | |
| else | |
| { | |
| return 0; | |
| } | |
| } | |
| /******************************************************************************* | |
| * xTraceGetLastError | |
| * | |
| * Gives the last error message, if any. NULL if no error message is stored. | |
| * Any error message is also presented when opening a trace file. | |
| ******************************************************************************/ | |
| const char* xTraceGetLastError(void) | |
| { | |
| return traceErrorMessage; | |
| } | |
| /******************************************************************************* | |
| * vTraceClearError | |
| * | |
| * Removes any previous error message generated by recorder calling prvTraceError. | |
| * By calling this function, it may be possible to start/restart the trace | |
| * despite errors in the recorder, but there is no guarantee that the trace | |
| * recorder will work correctly in that case, depending on the type of error. | |
| ******************************************************************************/ | |
| void vTraceClearError(void) | |
| { | |
| traceErrorMessage = NULL; | |
| if (RecorderDataPtr != NULL) | |
| { | |
| RecorderDataPtr->internalErrorOccured = 0; | |
| } | |
| } | |
| /******************************************************************************* | |
| * xTraceGetTraceBuffer | |
| * | |
| * Returns a pointer to the recorder data structure. Use this together with | |
| * uiTraceGetTraceBufferSize if you wish to implement an own store/upload | |
| * solution, e.g., in case a debugger connection is not available for uploading | |
| * the data. | |
| ******************************************************************************/ | |
| void* xTraceGetTraceBuffer(void) | |
| { | |
| return RecorderDataPtr; | |
| } | |
| /******************************************************************************* | |
| * uiTraceGetTraceBufferSize | |
| * | |
| * Gets the size of the recorder data structure. For use together with | |
| * vTraceGetTraceBuffer if you wish to implement an own store/upload solution, | |
| * e.g., in case a debugger connection is not available for uploading the data. | |
| ******************************************************************************/ | |
| uint32_t uiTraceGetTraceBufferSize(void) | |
| { | |
| return sizeof(RecorderDataType); | |
| } | |
| /****************************************************************************** | |
| * prvTraceTaskInstanceFinish | |
| * | |
| * Private common function for the vTraceTaskInstanceFinishXXX functions. | |
| *****************************************************************************/ | |
| static void prvTraceTaskInstanceFinish(int8_t direct) | |
| { | |
| TaskInstanceStatusEvent* tis; | |
| uint8_t dts45; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| dts45 = (uint8_t)prvTraceGetDTS(0xFF); | |
| tis = (TaskInstanceStatusEvent*) prvTraceNextFreeEventBufferSlot(); | |
| if (tis != NULL) | |
| { | |
| if (direct == 0) | |
| tis->type = TASK_INSTANCE_FINISHED_NEXT_KSE; | |
| else | |
| tis->type = TASK_INSTANCE_FINISHED_DIRECT; | |
| tis->dts = dts45; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /****************************************************************************** | |
| * vTraceInstanceFinishedNext(void) | |
| * | |
| * Marks the current task instance as finished on the next kernel call. | |
| * | |
| * If that kernel call is blocking, the instance ends after the blocking event | |
| * and the corresponding return event is then the start of the next instance. | |
| * If the kernel call is not blocking, the viewer instead splits the current | |
| * fragment right before the kernel call, which makes this call the first event | |
| * of the next instance. | |
| * | |
| * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h | |
| * | |
| * Example: | |
| * | |
| * while(1) | |
| * { | |
| * xQueueReceive(CommandQueue, &command, timeoutDuration); | |
| * processCommand(command); | |
| * vTraceInstanceFinishedNext(); | |
| * } | |
| *****************************************************************************/ | |
| void vTraceInstanceFinishedNext(void) | |
| { | |
| prvTraceTaskInstanceFinish(0); | |
| } | |
| /****************************************************************************** | |
| * vTraceInstanceFinishedNow(void) | |
| * | |
| * Marks the current task instance as finished at this very instant. | |
| * This makes the viewer to splits the current fragment at this point and begin | |
| * a new actor instance. | |
| * | |
| * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h | |
| * | |
| * Example: | |
| * | |
| * This example will generate two instances for each loop iteration. | |
| * The first instance ends at vTraceInstanceFinishedNow(), while the second | |
| * instance ends at the next xQueueReceive call. | |
| * | |
| * while (1) | |
| * { | |
| * xQueueReceive(CommandQueue, &command, timeoutDuration); | |
| * ProcessCommand(command); | |
| * vTraceInstanceFinishedNow(); | |
| * DoSometingElse(); | |
| * vTraceInstanceFinishedNext(); | |
| * } | |
| *****************************************************************************/ | |
| void vTraceInstanceFinishedNow(void) | |
| { | |
| prvTraceTaskInstanceFinish(1); | |
| } | |
| /******************************************************************************* | |
| * Interrupt recording functions | |
| ******************************************************************************/ | |
| #if (TRC_CFG_INCLUDE_ISR_TRACING == 1) | |
| /******************************************************************************* | |
| * xTraceSetISRProperties | |
| * | |
| * Stores a name and priority level for an Interrupt Service Routine, to allow | |
| * for better visualization. Returns a traceHandle used by vTraceStoreISRBegin. | |
| * | |
| * Example: | |
| * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt | |
| * ... | |
| * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); | |
| * ... | |
| * void ISR_handler() | |
| * { | |
| * vTraceStoreISRBegin(Timer1Handle); | |
| * ... | |
| * vTraceStoreISREnd(0); | |
| * } | |
| ******************************************************************************/ | |
| traceHandle xTraceSetISRProperties(const char* name, uint8_t priority) | |
| { | |
| static traceHandle handle = 0; | |
| TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0); | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "xTraceSetISRProperties: Invalid value for handle", 0); | |
| TRACE_ASSERT(name != NULL, "xTraceSetISRProperties: name == NULL", 0); | |
| handle++; | |
| prvTraceSetObjectName(TRACE_CLASS_ISR, handle, name); | |
| prvTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority); | |
| return handle; | |
| } | |
| /******************************************************************************* | |
| * vTraceStoreISRBegin | |
| * | |
| * Registers the beginning of an Interrupt Service Routine, using a traceHandle | |
| * provided by xTraceSetISRProperties. | |
| * | |
| * Example: | |
| * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt | |
| * ... | |
| * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); | |
| * ... | |
| * void ISR_handler() | |
| * { | |
| * vTraceStoreISRBegin(Timer1Handle); | |
| * ... | |
| * vTraceStoreISREnd(0); | |
| * } | |
| ******************************************************************************/ | |
| void vTraceStoreISRBegin(traceHandle handle) | |
| { | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("vTraceStoreISRBegin - recorder busy! See code comment."); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| uint16_t dts4; | |
| TRACE_ASSERT(handle != 0, "vTraceStoreISRBegin: Invalid ISR handle (NULL)", TRC_UNUSED); | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid ISR handle (> NISR)", TRC_UNUSED); | |
| dts4 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| if (nISRactive < TRC_CFG_MAX_ISR_NESTING) | |
| { | |
| TSEvent* ts; | |
| uint8_t hnd8 = prvTraceGet8BitHandle(handle); | |
| isrstack[nISRactive] = handle; | |
| nISRactive++; | |
| ts = (TSEvent*)prvTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| ts->type = TS_ISR_BEGIN; | |
| ts->dts = dts4; | |
| ts->objHandle = hnd8; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| else | |
| { | |
| /* This should not occur unless something is very wrong */ | |
| prvTraceError("Too many nested interrupts!"); | |
| } | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * vTraceStoreISREnd | |
| * | |
| * Registers the end of an Interrupt Service Routine. | |
| * | |
| * The parameter pendingISR indicates if the interrupt has requested a | |
| * task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the | |
| * interrupt is assumed to return to the previous context. | |
| * | |
| * Example: | |
| * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt | |
| * traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder | |
| * ... | |
| * traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1); | |
| * ... | |
| * void ISR_handler() | |
| * { | |
| * vTraceStoreISRBegin(traceHandleIsrTimer1); | |
| * ... | |
| * vTraceStoreISREnd(0); | |
| * } | |
| ******************************************************************************/ | |
| void vTraceStoreISREnd(int pendingISR) | |
| { | |
| TSEvent* ts; | |
| uint16_t dts5; | |
| uint8_t hnd8 = 0, type = 0; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| if (! RecorderDataPtr->recorderActive || ! handle_of_last_logged_task) | |
| { | |
| return; | |
| } | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("vTraceStoreISREnd - recorder busy! See code comment."); | |
| return; | |
| } | |
| if (nISRactive == 0) | |
| { | |
| prvTraceError("Unmatched call to vTraceStoreISREnd (nISRactive == 0, expected > 0)"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| isPendingContextSwitch |= pendingISR; /* Is there a pending context switch right now? */ | |
| nISRactive--; | |
| if (nISRactive > 0) | |
| { | |
| /* Return to another ISR */ | |
| type = TS_ISR_RESUME; | |
| hnd8 = prvTraceGet8BitHandle(isrstack[nISRactive - 1]); /* isrstack[nISRactive] is the handle of the ISR we're currently exiting. isrstack[nISRactive - 1] is the handle of the ISR that was executing previously. */ | |
| } | |
| else if ((isPendingContextSwitch == 0) || (prvTraceIsSchedulerSuspended())) | |
| { | |
| /* Return to interrupted task, if no context switch will occur in between. */ | |
| type = TS_TASK_RESUME; | |
| hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task); | |
| } | |
| if (type != 0) | |
| { | |
| dts5 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| ts = (TSEvent*)prvTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| ts->type = type; | |
| ts->objHandle = hnd8; | |
| ts->dts = dts5; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #else | |
| /* ISR tracing is turned off */ | |
| void prvTraceIncreaseISRActive(void) | |
| { | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| nISRactive++; | |
| } | |
| void prvTraceDecreaseISRActive(void) | |
| { | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| nISRactive--; | |
| } | |
| #endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1)*/ | |
| /********************************************************************************/ | |
| /* User Event functions */ | |
| /********************************************************************************/ | |
| #define MAX_ARG_SIZE (4+32) | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "writeInt8: buffer == NULL", 0); | |
| if (i >= MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint8_t*)buffer)[i] = value; | |
| if (i + 1 > MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| return ((uint8_t) (i + 1)); | |
| } | |
| #endif | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "writeInt16: buffer == NULL", 0); | |
| /* Align to multiple of 2 */ | |
| while ((i % 2) != 0) | |
| { | |
| if (i >= MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint8_t*)buffer)[i] = 0; | |
| i++; | |
| } | |
| if (i + 2 > MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint16_t*)buffer)[i/2] = value; | |
| return ((uint8_t) (i + 2)); | |
| } | |
| #endif | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "writeInt32: buffer == NULL", 0); | |
| /* A 32 bit value should begin at an even 4-byte address */ | |
| while ((i % 4) != 0) | |
| { | |
| if (i >= MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint8_t*)buffer)[i] = 0; | |
| i++; | |
| } | |
| if (i + 4 > MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint32_t*)buffer)[i/4] = value; | |
| return ((uint8_t) (i + 4)); | |
| } | |
| #endif | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT)) | |
| static uint8_t writeFloat(void * buffer, uint8_t i, float value) | |
| { | |
| TRACE_ASSERT(buffer != NULL, "writeFloat: buffer == NULL", 0); | |
| /* A 32 bit value should begin at an even 4-byte address */ | |
| while ((i % 4) != 0) | |
| { | |
| if (i >= MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint8_t*)buffer)[i] = 0; | |
| i++; | |
| } | |
| if (i + 4 > MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((float*)buffer)[i/4] = value; | |
| return i + 4; | |
| } | |
| #endif | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT)) | |
| static uint8_t writeDouble(void * buffer, uint8_t i, double value) | |
| { | |
| uint32_t * dest; | |
| uint32_t * src = (uint32_t*)&value; | |
| TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0); | |
| /* The double is written as two 32 bit values, and should begin at an even | |
| 4-byte address (to avoid having to align with 8 byte) */ | |
| while (i % 4 != 0) | |
| { | |
| if (i >= MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| ((uint8_t*)buffer)[i] = 0; | |
| i++; | |
| } | |
| if (i + 8 > MAX_ARG_SIZE) | |
| { | |
| return 255; | |
| } | |
| dest = &(((uint32_t *)buffer)[i/4]); | |
| dest[0] = src[0]; | |
| dest[1] = src[1]; | |
| return i + 8; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceUserEventFormat | |
| * | |
| * Parses the format string and stores the arguments in the buffer. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| static uint8_t prvTraceUserEventFormat(const char* formatStr, va_list vl, uint8_t* buffer, uint8_t byteOffset) | |
| { | |
| uint16_t formatStrIndex = 0; | |
| uint8_t argCounter = 0; | |
| uint8_t i = byteOffset; | |
| while (formatStr[formatStrIndex] != '\0') | |
| { | |
| if (formatStr[formatStrIndex] == '%') | |
| { | |
| argCounter++; | |
| if (argCounter > 15) | |
| { | |
| prvTraceError("vTracePrintF - Too many arguments, max 15 allowed!"); | |
| return 0; | |
| } | |
| formatStrIndex++; | |
| while ((formatStr[formatStrIndex] >= '0' && formatStr[formatStrIndex] <= '9') || formatStr[formatStrIndex] == '#' || formatStr[formatStrIndex] == '.') | |
| formatStrIndex++; | |
| if (formatStr[formatStrIndex] != '\0') | |
| { | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| case 'd': i = writeInt32( buffer, | |
| i, | |
| (uint32_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 'x': | |
| case 'X': | |
| case 'u': i = writeInt32( buffer, | |
| i, | |
| (uint32_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 's': i = writeInt16( buffer, | |
| i, | |
| xTraceRegisterString((char*)va_arg(vl, char*))); | |
| break; | |
| #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT) | |
| /* Yes, "double" as type also in the float | |
| case. This since "float" is promoted into "double" | |
| by the va_arg stuff. */ | |
| case 'f': i = writeFloat( buffer, | |
| i, | |
| (float)va_arg(vl, double)); | |
| break; | |
| #else | |
| /* No support for floats, but attempt to store a float user event | |
| avoid a possible crash due to float reference. Instead store the | |
| data on uint_32 format (will not be displayed anyway). This is just | |
| to keep va_arg and i consistent. */ | |
| case 'f': i = writeInt32( buffer, | |
| i, | |
| (uint32_t)va_arg(vl, double)); | |
| break; | |
| #endif | |
| case 'l': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT) | |
| case 'f': i = writeDouble(buffer, | |
| i, | |
| (double)va_arg(vl, double)); | |
| break; | |
| #else | |
| /* No support for floats, but attempt to store a float user event | |
| avoid a possible crash due to float reference. Instead store the | |
| data on uint_32 format (will not be displayed anyway). This is just | |
| to keep va_arg and i consistent. */ | |
| case 'f': i = writeInt32( buffer, /* In this case, the value will not be shown anyway */ | |
| i, | |
| (uint32_t)va_arg(vl, double)); | |
| i = writeInt32( buffer, /* Do it twice, to write in total 8 bytes */ | |
| i, | |
| (uint32_t)va_arg(vl, double)); | |
| break; | |
| #endif | |
| } | |
| break; | |
| case 'h': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| case 'd': i = writeInt16( buffer, | |
| i, | |
| (uint16_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 'u': i = writeInt16( buffer, | |
| i, | |
| (uint16_t)va_arg(vl, uint32_t)); | |
| break; | |
| } | |
| break; | |
| case 'b': | |
| formatStrIndex++; | |
| switch (formatStr[formatStrIndex]) | |
| { | |
| case 'd': i = writeInt8( buffer, | |
| i, | |
| (uint8_t)va_arg(vl, uint32_t)); | |
| break; | |
| case 'u': i = writeInt8( buffer, | |
| i, | |
| (uint8_t)va_arg(vl, uint32_t)); | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| else | |
| break; | |
| } | |
| formatStrIndex++; | |
| if (i == 255) | |
| { | |
| prvTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!"); | |
| return 0; | |
| } | |
| } | |
| return (uint8_t)(i+3)/4; | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceClearChannelBuffer | |
| * | |
| * Clears a number of items in the channel buffer, starting from nextSlotToWrite. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| static void prvTraceClearChannelBuffer(uint32_t count) | |
| { | |
| uint32_t slots; | |
| TRACE_ASSERT((TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) >= count, | |
| "prvTraceClearChannelBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED); | |
| /* Check if we're close to the end of the buffer */ | |
| if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE)) | |
| { | |
| slots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */ | |
| (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, slots); | |
| (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[0], 0, (count - slots)); | |
| } | |
| else | |
| (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, count); | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceCopyToDataBuffer | |
| * | |
| * Copies a number of items to the data buffer, starting from nextSlotToWrite. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count) | |
| { | |
| uint32_t slots; | |
| TRACE_ASSERT(data != NULL, | |
| "prvTraceCopyToDataBuffer: data == NULL.", TRC_UNUSED); | |
| TRACE_ASSERT(count <= (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE), | |
| "prvTraceCopyToDataBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED); | |
| /* Check if we're close to the end of the buffer */ | |
| if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE)) | |
| { | |
| slots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */ | |
| (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, slots * 4); | |
| (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[0], data + slots, (count - slots) * 4); | |
| } | |
| else | |
| { | |
| (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, count * 4); | |
| } | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceUBHelper1 | |
| * | |
| * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on | |
| * to the next helper function. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl) | |
| { | |
| uint32_t data[(3 + MAX_ARG_SIZE) / 4]; | |
| uint8_t byteOffset = 4; /* Need room for timestamp */ | |
| uint8_t noOfSlots; | |
| if (channel == 0) | |
| { | |
| /* We are dealing with an unknown channel format pair */ | |
| byteOffset = (uint8_t)(byteOffset + 4); /* Also need room for channel and format */ | |
| ((uint16_t*)data)[2] = eventLabel; | |
| ((uint16_t*)data)[3] = formatLabel; | |
| } | |
| noOfSlots = prvTraceUserEventFormat((char*)&(RecorderDataPtr->SymbolTable.symbytes[formatLabel+4]), vl, (uint8_t*)data, byteOffset); | |
| prvTraceUBHelper2(channel, data, noOfSlots); | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceUBHelper2 | |
| * | |
| * This function simply copies the data buffer to the actual user event buffer. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots) | |
| { | |
| static uint32_t old_timestamp = 0; | |
| uint32_t old_nextSlotToWrite = 0; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT((TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) >= noOfSlots, "prvTraceUBHelper2: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| /* Store the timestamp */ | |
| prvTracePortGetTimeStamp(data); | |
| if (*data < old_timestamp) | |
| { | |
| RecorderDataPtr->userEventBuffer.wraparoundCounter++; | |
| } | |
| old_timestamp = *data; | |
| /* Start by erasing any information in the channel buffer */ | |
| prvTraceClearChannelBuffer(noOfSlots); | |
| prvTraceCopyToDataBuffer(data, noOfSlots); /* Will wrap around the data if necessary */ | |
| old_nextSlotToWrite = RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Save the index that we want to write the channel data at when we're done */ | |
| RecorderDataPtr->userEventBuffer.nextSlotToWrite = (RecorderDataPtr->userEventBuffer.nextSlotToWrite + noOfSlots) % (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE); /* Make sure we never end up outside the buffer */ | |
| /* Write to the channel buffer to indicate that this user event is ready to be used */ | |
| if (channel != 0) | |
| { | |
| RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = channel; | |
| } | |
| else | |
| { | |
| /* 0xFF indicates that this is not a normal channel id */ | |
| RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (traceUBChannel)0xFF; | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * xTraceRegisterUBChannel | |
| * | |
| * Registers a channel for Separated User Events, i.e., those stored in the | |
| * separate user event buffer. | |
| * | |
| * Note: Only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is enabled in | |
| * trcSnapshotConfig.h | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| traceUBChannel xTraceRegisterUBChannel(traceString channel, traceString formatStr) | |
| { | |
| uint8_t i; | |
| traceUBChannel retVal = 0; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(formatStr != 0, "xTraceRegisterChannelFormat: formatStr == 0", (traceUBChannel)0); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| for (i = 1; i <= (TRC_CFG_UB_CHANNELS); i++) /* Size of the channels buffer is TRC_CFG_UB_CHANNELS + 1. Index 0 is unused. */ | |
| { | |
| if(RecorderDataPtr->userEventBuffer.channels[i].name == 0 && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == 0) | |
| { | |
| /* Found empty slot */ | |
| RecorderDataPtr->userEventBuffer.channels[i].name = channel; | |
| RecorderDataPtr->userEventBuffer.channels[i].defaultFormat = formatStr; | |
| retVal = (traceUBChannel)i; | |
| break; | |
| } | |
| if (RecorderDataPtr->userEventBuffer.channels[i].name == channel && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == formatStr) | |
| { | |
| /* Found a match */ | |
| retVal = (traceUBChannel)i; | |
| break; | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| return retVal; | |
| } | |
| #endif | |
| /****************************************************************************** | |
| * vTraceUBData | |
| * | |
| * Slightly faster version of vTracePrintF() due to no lookups. | |
| * | |
| * Note: This is only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is | |
| * enabled in trcSnapshotConfig.h | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| void vTraceUBData(traceUBChannel channelPair, ...) | |
| { | |
| va_list vl; | |
| TRACE_ASSERT(channelPair != 0, "vTraceUBData: Not a valid traceUBChannel!", TRC_UNUSED); | |
| va_start(vl, channelPair); | |
| vTraceUBData_Helper(channelPair, vl); | |
| va_end(vl); | |
| } | |
| #endif | |
| /* Extracts the channel name and format string from the traceUBChannel, then calls prvTraceUBHelper1. */ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl) | |
| { | |
| traceString channel; | |
| traceString formatStr; | |
| TRACE_ASSERT(channelPair != 0, "vTraceUBData_Helper: channelPair == 0", TRC_UNUSED); | |
| TRACE_ASSERT(channelPair <= (TRC_CFG_UB_CHANNELS), "vTraceUBData_Helper: ", TRC_UNUSED); | |
| channel = RecorderDataPtr->userEventBuffer.channels[channelPair].name; | |
| formatStr = RecorderDataPtr->userEventBuffer.channels[channelPair].defaultFormat; | |
| prvTraceUBHelper1(channelPair, channel, formatStr, vl); | |
| } | |
| #endif | |
| /****************************************************************************** | |
| * vTraceUBEvent | |
| * | |
| * Slightly faster version of ... due to no lookups. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)) | |
| void vTraceUBEvent(traceUBChannel channelPair) | |
| { | |
| uint32_t data[(3 + MAX_ARG_SIZE) / 4]; | |
| TRACE_ASSERT(channelPair != 0, "vTraceUBEvent: channelPair == 0", TRC_UNUSED); | |
| TRACE_ASSERT(channelPair <= (TRC_CFG_UB_CHANNELS), "vTraceUBEvent: ", TRC_UNUSED); | |
| prvTraceUBHelper2(channelPair, data, 1); /* Only need one slot for timestamp */ | |
| } | |
| #endif | |
| /****************************************************************************** | |
| * vTracePrintF | |
| * | |
| * Generates User Event with formatted text and data, similar to a "printf". | |
| * It is very fast compared to a normal "printf" since this function only | |
| * stores the arguments. The actual formatting is done | |
| * on the host PC when the trace is displayed in the viewer tool. | |
| * | |
| * User Event labels are created using xTraceRegisterString. | |
| * Example: | |
| * | |
| * traceString adc_uechannel = xTraceRegisterString("ADC User Events"); | |
| * ... | |
| * vTracePrintF(adc_uechannel, | |
| * "ADC channel %d: %lf volts", | |
| * ch, (double)adc_reading/(double)scale); | |
| * | |
| * This can be combined into one line, if desired, but this is slower: | |
| * | |
| * vTracePrintF(xTraceRegisterString("ADC User Events"), | |
| * "ADC channel %d: %lf volts", | |
| * ch, (double)adc_reading/(double)scale); | |
| * | |
| * Calling xTraceRegisterString multiple times will not create duplicate entries, but | |
| * it is of course faster to just do it once, and then keep the handle for later | |
| * use. If you don't have any data arguments, only a text label/string, it is | |
| * better to use vTracePrint - it is faster. | |
| * | |
| * Format specifiers supported: | |
| * %d - 32 bit signed integer | |
| * %u - 32 bit unsigned integer | |
| * %f - 32 bit float | |
| * %s - string (is copied to the recorder symbol table) | |
| * %hd - 16 bit signed integer | |
| * %hu - 16 bit unsigned integer | |
| * %bd - 8 bit signed integer | |
| * %bu - 8 bit unsigned integer | |
| * %lf - double-precision float (Note! See below...) | |
| * | |
| * Up to 15 data arguments are allowed, with a total size of maximum 32 byte. | |
| * In case this is exceeded, the user event is changed into an error message. | |
| * | |
| * The data is stored in trace buffer, and is packed to allow storing multiple | |
| * smaller data entries in the same 4-byte record, e.g., four 8-bit values. | |
| * A string requires two bytes, as the symbol table is limited to 64K. Storing | |
| * a double (%lf) uses two records, so this is quite costly. Use float (%f) | |
| * unless the higher precision is really necessary. | |
| * | |
| * Note that the double-precision float (%lf) assumes a 64 bit double | |
| * representation. This does not seem to be the case on e.g. PIC24 and PIC32. | |
| * Before using a %lf argument on a 16-bit MCU, please verify that | |
| * "sizeof(double)" actually gives 8 as expected. If not, use %f instead. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| void vTracePrintF(traceString eventLabel, const char* formatStr, ...) | |
| { | |
| va_list vl; | |
| va_start(vl, formatStr); | |
| vTracePrintF_Helper(eventLabel, formatStr, vl); | |
| va_end(vl); | |
| } | |
| #endif | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl) | |
| { | |
| #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0) | |
| uint32_t noOfSlots; | |
| UserEvent* ue1; | |
| uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4]; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(formatStr != NULL, "vTracePrintF_Helper: formatStr == NULL", TRC_UNUSED); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| /* First, write the "primary" user event entry in the local buffer, but | |
| let the event type be "EVENT_BEING_WRITTEN" for now...*/ | |
| ue1 = (UserEvent*)(&tempDataBuffer[0]); | |
| ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */ | |
| noOfSlots = prvTraceUserEventFormat(formatStr, vl, (uint8_t*)tempDataBuffer, 4); | |
| /* Store the format string, with a reference to the channel symbol */ | |
| ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel); | |
| ue1->dts = (uint8_t)prvTraceGetDTS(0xFF); | |
| /* prvTraceGetDTS might stop the recorder in some cases... */ | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| /* If the data does not fit in the remaining main buffer, wrap around to | |
| 0 if allowed, otherwise stop the recorder and quit). */ | |
| if (RecorderDataPtr->nextFreeIndex + noOfSlots > RecorderDataPtr->maxEvents) | |
| { | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4], | |
| 0, | |
| (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4); | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| RecorderDataPtr->bufferIsFull = 1; | |
| #else | |
| /* Stop recorder, since the event data will not fit in the | |
| buffer and not circular buffer in this case... */ | |
| vTraceStop(); | |
| #endif | |
| } | |
| /* Check if recorder has been stopped (i.e., vTraceStop above) */ | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| /* Check that the buffer to be overwritten does not contain any user | |
| events that would be partially overwritten. If so, they must be "killed" | |
| by replacing the user event and following data with NULL events (i.e., | |
| using a memset to zero).*/ | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| prvCheckDataToBeOverwrittenForMultiEntryEvents((uint8_t)noOfSlots); | |
| #endif | |
| /* Copy the local buffer to the main buffer */ | |
| (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4], | |
| tempDataBuffer, | |
| noOfSlots * 4); | |
| /* Update the event type, i.e., number of data entries following the | |
| main USER_EVENT entry (Note: important that this is after the memcpy, | |
| but within the critical section!)*/ | |
| RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4] = | |
| (uint8_t) ( USER_EVENT + noOfSlots - 1 ); | |
| /* Update the main buffer event index (already checked that it fits in | |
| the buffer, so no need to check for wrapping)*/ | |
| RecorderDataPtr->nextFreeIndex += noOfSlots; | |
| RecorderDataPtr->numEvents += noOfSlots; | |
| if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE)) | |
| { | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| /* We have reached the end, but this is a ring buffer. Start from the beginning again. */ | |
| RecorderDataPtr->bufferIsFull = 1; | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| #else | |
| /* We have reached the end so we stop. */ | |
| vTraceStop(); | |
| #endif | |
| } | |
| } | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| /* Make sure the next entry is cleared correctly */ | |
| prvCheckDataToBeOverwrittenForMultiEntryEvents(1); | |
| #endif | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| #elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| /* Use the separate user event buffer */ | |
| traceString formatLabel; | |
| traceUBChannel channel; | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| formatLabel = xTraceRegisterString(formatStr); | |
| channel = xTraceRegisterUBChannel(eventLabel, formatLabel); | |
| prvTraceUBHelper1(channel, eventLabel, formatLabel, vl); | |
| } | |
| #endif | |
| } | |
| #endif | |
| /****************************************************************************** | |
| * vTracePrint | |
| * | |
| * Basic user event | |
| * | |
| * Generates a User Event with a text label. The label is created/looked up | |
| * in the symbol table using xTraceRegisterString. | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| void vTracePrint(traceString chn, const char* str) | |
| { | |
| #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0) | |
| UserEvent* ue; | |
| uint8_t dts1; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| dts1 = (uint8_t)prvTraceGetDTS(0xFF); | |
| ue = (UserEvent*) prvTraceNextFreeEventBufferSlot(); | |
| if (ue != NULL) | |
| { | |
| ue->dts = dts1; | |
| ue->type = USER_EVENT; | |
| ue->payload = prvTraceOpenSymbol(str, chn); | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| #elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1) | |
| traceUBChannel channel; | |
| uint32_t noOfSlots = 1; | |
| uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4]; | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| traceString trcStr = prvTraceOpenSymbol(str, chn); | |
| channel = xTraceRegisterUBChannel(chn, trcStr); | |
| if (channel == 0) | |
| { | |
| /* We are dealing with an unknown channel format pair */ | |
| noOfSlots++; /* Also need room for channel and format */ | |
| ((uint16_t*)tempDataBuffer)[2] = chn; | |
| ((uint16_t*)tempDataBuffer)[3] = trcStr; | |
| } | |
| prvTraceUBHelper2(channel, tempDataBuffer, noOfSlots); | |
| } | |
| #endif | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * xTraceRegisterString | |
| * | |
| * Register strings in the recorder, e.g. for names of user event channels. | |
| * | |
| * Example: | |
| * myEventHandle = xTraceRegisterString("MyUserEvent"); | |
| * ... | |
| * vTracePrintF(myEventHandle, "My value is: %d", myValue); | |
| ******************************************************************************/ | |
| #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) | |
| traceString xTraceRegisterString(const char* label) | |
| { | |
| TRACE_ASSERT(label != NULL, "xTraceRegisterString: label == NULL", (traceString)0); | |
| TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0); | |
| return prvTraceOpenSymbol(label, 0); | |
| } | |
| #endif | |
| #if ((!defined TRC_CFG_INCLUDE_READY_EVENTS) || (TRC_CFG_INCLUDE_READY_EVENTS == 1)) | |
| void prvTraceSetReadyEventsEnabled(int status) | |
| { | |
| readyEventsEnabled = status; | |
| } | |
| /******************************************************************************* | |
| * prvTraceStoreTaskReady | |
| * | |
| * This function stores a ready state for the task handle sent in as parameter. | |
| ******************************************************************************/ | |
| void prvTraceStoreTaskReady(traceHandle handle) | |
| { | |
| uint16_t dts3; | |
| TREvent* tr; | |
| uint8_t hnd8; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| if (handle == 0) | |
| { | |
| /* On FreeRTOS v7.3.0, this occurs when creating tasks due to a bad | |
| placement of the trace macro. In that case, the events are ignored. */ | |
| return; | |
| } | |
| if (! readyEventsEnabled) | |
| { | |
| /* When creating tasks, ready events are also created. If creating | |
| a "hidden" (not traced) task, we must therefore disable recording | |
| of ready events to avoid an undesired ready event... */ | |
| return; | |
| } | |
| TRACE_ASSERT(handle <= (TRC_CFG_NTASK), "prvTraceStoreTaskReady: Invalid value for handle", TRC_UNUSED); | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("Recorder busy - high priority ISR using syscall? (1)"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */ | |
| { | |
| dts3 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| hnd8 = prvTraceGet8BitHandle(handle); | |
| tr = (TREvent*)prvTraceNextFreeEventBufferSlot(); | |
| if (tr != NULL) | |
| { | |
| tr->type = DIV_TASK_READY; | |
| tr->dts = dts3; | |
| tr->objHandle = hnd8; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceStoreLowPower | |
| * | |
| * This function stores a low power state. | |
| ******************************************************************************/ | |
| void prvTraceStoreLowPower(uint32_t flag) | |
| { | |
| uint16_t dts; | |
| LPEvent* lp; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(flag <= 1, "prvTraceStoreLowPower: Invalid flag value", TRC_UNUSED); | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("Recorder busy - high priority ISR using syscall? (1)"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| dts = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| lp = (LPEvent*)prvTraceNextFreeEventBufferSlot(); | |
| if (lp != NULL) | |
| { | |
| lp->type = (uint8_t) (LOW_POWER_BEGIN + ( uint8_t ) flag); /* BEGIN or END depending on flag */ | |
| lp->dts = dts; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| /******************************************************************************* | |
| * vTraceStoreMemMangEvent | |
| * | |
| * This function stores malloc and free events. Each call requires two records, | |
| * for size and address respectively. The event code parameter (ecode) is applied | |
| * to the first record (size) and the following address record gets event | |
| * code "ecode + 1", so make sure this is respected in the event code table. | |
| * Note: On "free" calls, the signed_size parameter should be negative. | |
| ******************************************************************************/ | |
| #if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1) | |
| #if (TRC_CFG_SCHEDULING_ONLY == 0) | |
| void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size) | |
| { | |
| uint8_t dts1; | |
| MemEventSize * ms; | |
| MemEventAddr * ma; | |
| uint16_t size_low; | |
| uint16_t addr_low; | |
| uint8_t addr_high; | |
| uint32_t size; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| if (RecorderDataPtr == NULL) | |
| { | |
| /* Occurs in vTraceInitTraceData, if using dynamic allocation. */ | |
| return; | |
| } | |
| if (signed_size < 0) | |
| size = (uint32_t)(- signed_size); | |
| else | |
| size = (uint32_t)(signed_size); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| heapMemUsage = heapMemUsage + (uint32_t)signed_size; | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| dts1 = (uint8_t)prvTraceGetDTS(0xFF); | |
| size_low = (uint16_t)prvTraceGetParam(0xFFFF, size); | |
| ms = (MemEventSize *)prvTraceNextFreeEventBufferSlot(); | |
| if (ms != NULL) | |
| { | |
| ms->dts = dts1; | |
| ms->type = NULL_EVENT; /* Updated when all events are written */ | |
| ms->size = size_low; | |
| prvTraceUpdateCounters(); | |
| /* Storing a second record with address (signals "failed" if null) */ | |
| #if (TRC_CFG_HEAP_SIZE_BELOW_16M) | |
| /* If the heap address range is within 16 MB, i.e., the upper 8 bits | |
| of addresses are constant, this optimization avoids storing an extra | |
| event record by ignoring the upper 8 bit of the address */ | |
| addr_low = address & 0xFFFF; | |
| addr_high = (address >> 16) & 0xFF; | |
| #else | |
| /* The whole 32 bit address is stored using a second event record | |
| for the upper 16 bit */ | |
| addr_low = (uint16_t)prvTraceGetParam(0xFFFF, address); | |
| addr_high = 0; | |
| #endif | |
| ma = (MemEventAddr *) prvTraceNextFreeEventBufferSlot(); | |
| if (ma != NULL) | |
| { | |
| ma->addr_low = addr_low; | |
| ma->addr_high = addr_high; | |
| ma->type = (uint8_t) (ecode + 1); /* Note this! */ | |
| ms->type = (uint8_t) ecode; | |
| prvTraceUpdateCounters(); | |
| RecorderDataPtr->heapMemUsage = heapMemUsage; | |
| } | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #endif /* TRC_CFG_SCHEDULING_ONLY */ | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceStoreKernelCall | |
| * | |
| * This is the main integration point for storing kernel calls, and | |
| * is called by the hooks in trcKernelHooks.h (see trcKernelPort.h for event codes). | |
| ******************************************************************************/ | |
| #if (TRC_CFG_SCHEDULING_ONLY == 0) | |
| void prvTraceStoreKernelCall(uint32_t ecode, traceObjectClass objectClass, uint32_t objectNumber) | |
| { | |
| KernelCall * kse; | |
| uint16_t dts1; | |
| uint8_t hnd8; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(ecode < 0xFF, "prvTraceStoreKernelCall: ecode >= 0xFF", TRC_UNUSED); | |
| TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCall: objectClass >= TRACE_NCLASSES", TRC_UNUSED); | |
| TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCall: Invalid value for objectNumber", TRC_UNUSED); | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("Recorder busy - high priority ISR using syscall? (2)"); | |
| return; | |
| } | |
| if (handle_of_last_logged_task == 0) | |
| { | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| dts1 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber); | |
| kse = (KernelCall*) prvTraceNextFreeEventBufferSlot(); | |
| if (kse != NULL) | |
| { | |
| kse->dts = dts1; | |
| kse->type = (uint8_t)ecode; | |
| kse->objHandle = hnd8; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #endif /* TRC_CFG_SCHEDULING_ONLY */ | |
| /******************************************************************************* | |
| * prvTraceStoreKernelCallWithParam | |
| * | |
| * Used for storing kernel calls with a handle and a numeric parameter. If the | |
| * numeric parameter does not fit in one byte, and extra XPS event is inserted | |
| * before the kernel call event containing the three upper bytes. | |
| ******************************************************************************/ | |
| #if (TRC_CFG_SCHEDULING_ONLY == 0) | |
| void prvTraceStoreKernelCallWithParam(uint32_t evtcode, | |
| traceObjectClass objectClass, | |
| uint32_t objectNumber, | |
| uint32_t param) | |
| { | |
| KernelCallWithParamAndHandle * kse; | |
| uint8_t dts2; | |
| uint8_t hnd8; | |
| uint8_t p8; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithParam: evtcode >= 0xFF", TRC_UNUSED); | |
| TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCallWithParam: objectClass >= TRACE_NCLASSES", TRC_UNUSED); | |
| TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCallWithParam: Invalid value for objectNumber", TRC_UNUSED); | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("Recorder busy - high priority ISR using syscall? (3)"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| dts2 = (uint8_t)prvTraceGetDTS(0xFF); | |
| p8 = (uint8_t) prvTraceGetParam(0xFF, param); | |
| hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber); | |
| kse = (KernelCallWithParamAndHandle*) prvTraceNextFreeEventBufferSlot(); | |
| if (kse != NULL) | |
| { | |
| kse->dts = dts2; | |
| kse->type = (uint8_t)evtcode; | |
| kse->objHandle = hnd8; | |
| kse->param = p8; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #endif /* TRC_CFG_SCHEDULING_ONLY */ | |
| /******************************************************************************* | |
| * prvTraceGetParam | |
| * | |
| * Used for storing extra bytes for kernel calls with numeric parameters. | |
| * | |
| * May only be called within a critical section! | |
| ******************************************************************************/ | |
| #if (TRC_CFG_SCHEDULING_ONLY == 0) | |
| static uint32_t prvTraceGetParam(uint32_t param_max, uint32_t param) | |
| { | |
| XPSEvent* xps; | |
| TRACE_ASSERT(param_max == 0xFF || param_max == 0xFFFF, | |
| "prvTraceGetParam: Invalid value for param_max", param); | |
| if (param <= param_max) | |
| { | |
| return param; | |
| } | |
| else | |
| { | |
| xps = (XPSEvent*) prvTraceNextFreeEventBufferSlot(); | |
| if (xps != NULL) | |
| { | |
| xps->type = DIV_XPS; | |
| xps->xps_8 = (uint8_t)((param & (0xFF00 & ~param_max)) >> 8); | |
| xps->xps_16 = (uint16_t)((param & (0xFFFF0000 & ~param_max)) >> 16); | |
| prvTraceUpdateCounters(); | |
| } | |
| return param & param_max; | |
| } | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceStoreKernelCallWithNumericParamOnly | |
| * | |
| * Used for storing kernel calls with numeric parameters only. This is | |
| * only used for traceTASK_DELAY and traceDELAY_UNTIL at the moment. | |
| ******************************************************************************/ | |
| #if (TRC_CFG_SCHEDULING_ONLY == 0) | |
| void prvTraceStoreKernelCallWithNumericParamOnly(uint32_t evtcode, uint32_t param) | |
| { | |
| KernelCallWithParam16 * kse; | |
| uint8_t dts6; | |
| uint16_t restParam; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| restParam = 0; | |
| TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithNumericParamOnly: Invalid value for evtcode", TRC_UNUSED); | |
| if (recorder_busy) | |
| { | |
| /************************************************************************* | |
| * This occurs if an ISR calls a trace function, preempting a previous | |
| * trace call that is being processed in a different ISR or task. | |
| * If this occurs, there is probably a problem in the definition of the | |
| * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and | |
| * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt | |
| * and any other ISRs that calls the trace recorder directly or via | |
| * traced kernel functions. The ARM port disables all interrupts using the | |
| * PRIMASK register to avoid this issue. | |
| *************************************************************************/ | |
| prvTraceError("Recorder busy - high priority ISR using syscall? (4)"); | |
| return; | |
| } | |
| trcCRITICAL_SECTION_BEGIN(); | |
| if (RecorderDataPtr->recorderActive && handle_of_last_logged_task) | |
| { | |
| dts6 = (uint8_t)prvTraceGetDTS(0xFF); | |
| restParam = (uint16_t)prvTraceGetParam(0xFFFF, param); | |
| kse = (KernelCallWithParam16*) prvTraceNextFreeEventBufferSlot(); | |
| if (kse != NULL) | |
| { | |
| kse->dts = dts6; | |
| kse->type = (uint8_t)evtcode; | |
| kse->param = restParam; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| } | |
| #endif /* TRC_CFG_SCHEDULING_ONLY */ | |
| /******************************************************************************* | |
| * prvTraceStoreTaskswitch | |
| * Called by the scheduler from the SWITCHED_OUT hook, and by uiTraceStart. | |
| * At this point interrupts are assumed to be disabled! | |
| ******************************************************************************/ | |
| void prvTraceStoreTaskswitch(traceHandle task_handle) | |
| { | |
| uint16_t dts3; | |
| TSEvent* ts; | |
| uint8_t hnd8; | |
| #if (TRC_CFG_INCLUDE_ISR_TRACING == 1) | |
| extern int32_t isPendingContextSwitch; | |
| #endif | |
| trcSR_ALLOC_CRITICAL_SECTION_ON_CORTEX_M_ONLY(); | |
| TRACE_ASSERT(task_handle <= (TRC_CFG_NTASK), | |
| "prvTraceStoreTaskswitch: Invalid value for task_handle", TRC_UNUSED); | |
| trcCRITICAL_SECTION_BEGIN_ON_CORTEX_M_ONLY(); | |
| if ((task_handle != handle_of_last_logged_task) && (RecorderDataPtr->recorderActive)) | |
| { | |
| #if (TRC_CFG_INCLUDE_ISR_TRACING == 1) | |
| isPendingContextSwitch = 0; | |
| #endif | |
| dts3 = (uint16_t)prvTraceGetDTS(0xFFFF); | |
| handle_of_last_logged_task = task_handle; | |
| hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task); | |
| ts = (TSEvent*)prvTraceNextFreeEventBufferSlot(); | |
| if (ts != NULL) | |
| { | |
| if (prvTraceGetObjectState(TRACE_CLASS_TASK, | |
| handle_of_last_logged_task) == TASK_STATE_INSTANCE_ACTIVE) | |
| { | |
| ts->type = TS_TASK_RESUME; | |
| } | |
| else | |
| { | |
| ts->type = TS_TASK_BEGIN; | |
| } | |
| ts->dts = dts3; | |
| ts->objHandle = hnd8; | |
| prvTraceSetObjectState(TRACE_CLASS_TASK, | |
| handle_of_last_logged_task, | |
| TASK_STATE_INSTANCE_ACTIVE); | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| trcCRITICAL_SECTION_END_ON_CORTEX_M_ONLY(); | |
| } | |
| /******************************************************************************* | |
| * prvTraceStoreObjectNameOnCloseEvent | |
| * | |
| * Updates the symbol table with the name of this object from the dynamic | |
| * objects table and stores a "close" event, holding the mapping between handle | |
| * and name (a symbol table handle). The stored name-handle mapping is thus the | |
| * "old" one, valid up until this point. | |
| ******************************************************************************/ | |
| void prvTraceStoreObjectNameOnCloseEvent(uint8_t evtcode, traceHandle handle, | |
| traceObjectClass objectclass) | |
| { | |
| ObjCloseNameEvent * ce; | |
| const char * name; | |
| traceString idx; | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceStoreObjectNameOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED); | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceStoreObjectNameOnCloseEvent: Invalid value for handle", TRC_UNUSED); | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| uint8_t hnd8 = prvTraceGet8BitHandle(handle); | |
| name = TRACE_PROPERTY_NAME_GET(objectclass, handle); | |
| idx = prvTraceOpenSymbol(name, 0); | |
| // Interrupt disable not necessary, already done in trcHooks.h macro | |
| ce = (ObjCloseNameEvent*) prvTraceNextFreeEventBufferSlot(); | |
| if (ce != NULL) | |
| { | |
| ce->type = (uint8_t) evtcode; | |
| ce->objHandle = hnd8; | |
| ce->symbolIndex = idx; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| } | |
| void prvTraceStoreObjectPropertiesOnCloseEvent(uint8_t evtcode, traceHandle handle, | |
| traceObjectClass objectclass) | |
| { | |
| ObjClosePropEvent * pe; | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceStoreObjectPropertiesOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED); | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceStoreObjectPropertiesOnCloseEvent: Invalid value for handle", TRC_UNUSED); | |
| if (RecorderDataPtr->recorderActive) | |
| { | |
| // Interrupt disable not necessary, already done in trcHooks.h macro | |
| pe = (ObjClosePropEvent*) prvTraceNextFreeEventBufferSlot(); | |
| if (pe != NULL) | |
| { | |
| if (objectclass == TRACE_CLASS_TASK) | |
| { | |
| pe->arg1 = TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, handle); | |
| } | |
| else | |
| { | |
| pe->arg1 = TRACE_PROPERTY_OBJECT_STATE(objectclass, handle); | |
| } | |
| pe->type = evtcode; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| } | |
| void prvTraceSetPriorityProperty(uint8_t objectclass, traceHandle id, uint8_t value) | |
| { | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceSetPriorityProperty: objectclass >= TRACE_NCLASSES", TRC_UNUSED); | |
| TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceSetPriorityProperty: Invalid value for id", TRC_UNUSED); | |
| TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id) = value; | |
| } | |
| uint8_t prvTraceGetPriorityProperty(uint8_t objectclass, traceHandle id) | |
| { | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceGetPriorityProperty: objectclass >= TRACE_NCLASSES", 0); | |
| TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceGetPriorityProperty: Invalid value for id", 0); | |
| return TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id); | |
| } | |
| void prvTraceSetObjectState(uint8_t objectclass, traceHandle id, uint8_t value) | |
| { | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceSetObjectState: objectclass >= TRACE_NCLASSES", TRC_UNUSED); | |
| TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceSetObjectState: Invalid value for id", TRC_UNUSED); | |
| TRACE_PROPERTY_OBJECT_STATE(objectclass, id) = value; | |
| } | |
| uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id) | |
| { | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceGetObjectState: objectclass >= TRACE_NCLASSES", 0); | |
| TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceGetObjectState: Invalid value for id", 0); | |
| return TRACE_PROPERTY_OBJECT_STATE(objectclass, id); | |
| } | |
| void prvTraceSetTaskInstanceFinished(traceHandle handle) | |
| { | |
| TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_TASK], | |
| "prvTraceSetTaskInstanceFinished: Invalid value for handle", TRC_UNUSED); | |
| #if (TRC_CFG_USE_IMPLICIT_IFE_RULES == 1) | |
| TRACE_PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle) = 0; | |
| #endif | |
| } | |
| /******************************************************************************* | |
| * Static data initializations | |
| ******************************************************************************/ | |
| /* A set of stacks that keeps track of available object handles for each class. | |
| The stacks are empty initially, meaning that allocation of new handles will be | |
| based on a counter (for each object class). Any delete operation will | |
| return the handle to the corresponding stack, for reuse on the next allocate.*/ | |
| objectHandleStackType objectHandleStacks = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }; | |
| /* Initial TRC_HWTC_COUNT value, for detecting if the time-stamping source is | |
| enabled. If using the OS periodic timer for time-stamping, this might not | |
| have been configured on the earliest events during the startup. */ | |
| uint32_t init_hwtc_count; | |
| /******************************************************************************* | |
| * RecorderData | |
| * | |
| * The main data structure in snapshot mode, when using the default static memory | |
| * allocation (TRC_RECORDER_BUFFER_ALLOCATION_STATIC). The recorder uses a pointer | |
| * RecorderDataPtr to access the data, to also allow for dynamic or custom data | |
| * allocation (see TRC_CFG_RECORDER_BUFFER_ALLOCATION). | |
| ******************************************************************************/ | |
| #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC) | |
| RecorderDataType RecorderData; | |
| #endif | |
| /******************************************************************************* | |
| * RecorderDataPtr | |
| * | |
| * Pointer to the main data structure, when in snapshot mode. | |
| ******************************************************************************/ | |
| RecorderDataType* RecorderDataPtr = NULL; | |
| /* This version of the function dynamically allocates the trace data */ | |
| void prvTraceInitTraceData() | |
| { | |
| if (RecorderDataPtr == NULL) | |
| { | |
| #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC) | |
| RecorderDataPtr = &RecorderData; | |
| #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_DYNAMIC) | |
| RecorderDataPtr = (RecorderDataType*)TRACE_MALLOC(sizeof(RecorderDataType)); | |
| if (! RecorderDataPtr) | |
| { | |
| prvTraceError("Failed allocating recorder buffer!"); | |
| return; | |
| } | |
| #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM) | |
| if (! RecorderDataPtr) | |
| { | |
| prvTraceError("Recorder data pointer not set! Use vTraceSetRecorderDataBuffer()."); | |
| return; | |
| } | |
| #endif | |
| } | |
| else | |
| { | |
| if (RecorderDataPtr->startmarker0 == 1) | |
| { | |
| /* Already initialized */ | |
| return; | |
| } | |
| } | |
| init_hwtc_count = TRC_HWTC_COUNT; | |
| (void)memset(RecorderDataPtr, 0, sizeof(RecorderDataType)); | |
| RecorderDataPtr->version = TRACE_KERNEL_VERSION; | |
| RecorderDataPtr->minor_version = TRACE_MINOR_VERSION; | |
| RecorderDataPtr->irq_priority_order = TRC_IRQ_PRIORITY_ORDER; | |
| RecorderDataPtr->filesize = sizeof(RecorderDataType); | |
| RecorderDataPtr->maxEvents = (TRC_CFG_EVENT_BUFFER_SIZE); | |
| RecorderDataPtr->debugMarker0 = (int32_t) 0xF0F0F0F0; | |
| RecorderDataPtr->isUsing16bitHandles = TRC_CFG_USE_16BIT_OBJECT_HANDLES; | |
| RecorderDataPtr->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD; | |
| /* This function is kernel specific */ | |
| vTraceInitObjectPropertyTable(); | |
| RecorderDataPtr->debugMarker1 = (int32_t)0xF1F1F1F1; | |
| RecorderDataPtr->SymbolTable.symTableSize = (TRC_CFG_SYMBOL_TABLE_SIZE); | |
| RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1; | |
| #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT == 1) | |
| RecorderDataPtr->exampleFloatEncoding = 1.0f; /* otherwise already zero */ | |
| #endif | |
| RecorderDataPtr->debugMarker2 = (int32_t)0xF2F2F2F2; | |
| prvStrncpy(RecorderDataPtr->systemInfo, "Trace Recorder Demo", 80); | |
| RecorderDataPtr->debugMarker3 = (int32_t)0xF3F3F3F3; | |
| RecorderDataPtr->endmarker0 = 0x0A; | |
| RecorderDataPtr->endmarker1 = 0x0B; | |
| RecorderDataPtr->endmarker2 = 0x0C; | |
| RecorderDataPtr->endmarker3 = 0x0D; | |
| RecorderDataPtr->endmarker4 = 0x71; | |
| RecorderDataPtr->endmarker5 = 0x72; | |
| RecorderDataPtr->endmarker6 = 0x73; | |
| RecorderDataPtr->endmarker7 = 0x74; | |
| RecorderDataPtr->endmarker8 = 0xF1; | |
| RecorderDataPtr->endmarker9 = 0xF2; | |
| RecorderDataPtr->endmarker10 = 0xF3; | |
| RecorderDataPtr->endmarker11 = 0xF4; | |
| #if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER | |
| RecorderDataPtr->userEventBuffer.bufferID = 1; | |
| RecorderDataPtr->userEventBuffer.version = 0; | |
| RecorderDataPtr->userEventBuffer.numberOfSlots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE); | |
| RecorderDataPtr->userEventBuffer.numberOfChannels = (TRC_CFG_UB_CHANNELS) + 1; | |
| #endif | |
| /* Kernel specific initialization of the objectHandleStacks variable */ | |
| vTraceInitObjectHandleStack(); | |
| /* Finally, the 12-byte "start markers" are initialized, allowing for | |
| Tracealyzer to find the trace data in a larger RAM dump. | |
| The start and end markers must be unique, but without proper precautions there | |
| might be a risk of accidental duplicates of the start/end markers, e.g., due to | |
| compiler optimizations. | |
| The below initialization of the start marker is therefore made in reverse order | |
| and the fields are volatile to ensure this assignment order. This to avoid any | |
| chance of accidental duplicates of this elsewhere in memory. | |
| Moreover, the fields are set byte-by-byte to avoid endian issues.*/ | |
| RecorderDataPtr->startmarker11 = 0xF4; | |
| RecorderDataPtr->startmarker10 = 0xF3; | |
| RecorderDataPtr->startmarker9 = 0xF2; | |
| RecorderDataPtr->startmarker8 = 0xF1; | |
| RecorderDataPtr->startmarker7 = 0x74; | |
| RecorderDataPtr->startmarker6 = 0x73; | |
| RecorderDataPtr->startmarker5 = 0x72; | |
| RecorderDataPtr->startmarker4 = 0x71; | |
| RecorderDataPtr->startmarker3 = 0x04; | |
| RecorderDataPtr->startmarker2 = 0x03; | |
| RecorderDataPtr->startmarker1 = 0x02; | |
| RecorderDataPtr->startmarker0 = 0x01; | |
| if (traceErrorMessage != NULL) | |
| { | |
| // An error was detected before vTraceEnable was called, make sure this is stored in the trace data. | |
| prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80); | |
| RecorderDataPtr->internalErrorOccured = 1; | |
| vTraceStop(); | |
| } | |
| #ifdef TRC_PORT_SPECIFIC_INIT | |
| TRC_PORT_SPECIFIC_INIT(); | |
| #endif | |
| } | |
| void* prvTraceNextFreeEventBufferSlot(void) | |
| { | |
| if (! RecorderDataPtr->recorderActive) | |
| { | |
| /* If an XTS or XPS event prior to the main event has filled the buffer | |
| before saving the main event, and store mode is "stop when full". */ | |
| return NULL; | |
| } | |
| if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE)) | |
| { | |
| prvTraceError("Attempt to index outside event buffer!"); | |
| return NULL; | |
| } | |
| return (void*)(&RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex*4]); | |
| } | |
| uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass) | |
| { | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "uiIndexOfObject: Invalid value for objectclass", 0); | |
| TRACE_ASSERT(objecthandle > 0 && objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "uiIndexOfObject: Invalid value for objecthandle", 0); | |
| if ((objectclass < TRACE_NCLASSES) && (objecthandle > 0) && | |
| (objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass])) | |
| { | |
| return (uint16_t)(RecorderDataPtr->ObjectPropertyTable.StartIndexOfClass[objectclass] + | |
| (RecorderDataPtr->ObjectPropertyTable.TotalPropertyBytesPerClass[objectclass] * (objecthandle-1))); | |
| } | |
| prvTraceError("Object table lookup with invalid object handle or object class!"); | |
| return 0; | |
| } | |
| traceHandle prvTraceGetObjectHandle(traceObjectClass objectclass) | |
| { | |
| traceHandle handle; | |
| static int indexOfHandle; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0); | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceGetObjectHandle: Invalid value for objectclass", (traceHandle)0); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass]; | |
| if (objectHandleStacks.objectHandles[indexOfHandle] == 0) | |
| { | |
| /* Zero is used to indicate a never before used handle, i.e., | |
| new slots in the handle stack. The handle slot needs to | |
| be initialized here (starts at 1). */ | |
| objectHandleStacks.objectHandles[indexOfHandle] = | |
| (traceHandle)(1 + indexOfHandle - | |
| objectHandleStacks.lowestIndexOfClass[objectclass]); | |
| } | |
| handle = objectHandleStacks.objectHandles[indexOfHandle]; | |
| if (objectHandleStacks.indexOfNextAvailableHandle[objectclass] | |
| > objectHandleStacks.highestIndexOfClass[objectclass]) | |
| { | |
| prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass)); | |
| handle = 0; | |
| } | |
| else | |
| { | |
| int hndCount; | |
| objectHandleStacks.indexOfNextAvailableHandle[objectclass]++; | |
| hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] - | |
| objectHandleStacks.lowestIndexOfClass[objectclass]; | |
| if (hndCount > | |
| objectHandleStacks.handleCountWaterMarksOfClass[objectclass]) | |
| { | |
| objectHandleStacks.handleCountWaterMarksOfClass[objectclass] = | |
| (traceHandle)hndCount; | |
| } | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| return handle; | |
| } | |
| void prvTraceFreeObjectHandle(traceObjectClass objectclass, traceHandle handle) | |
| { | |
| int indexOfHandle; | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, | |
| "prvTraceFreeObjectHandle: Invalid value for objectclass", TRC_UNUSED); | |
| TRACE_ASSERT(handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], | |
| "prvTraceFreeObjectHandle: Invalid value for handle", TRC_UNUSED); | |
| /* Check that there is room to push the handle on the stack */ | |
| if ((objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) < | |
| objectHandleStacks.lowestIndexOfClass[objectclass]) | |
| { | |
| /* Error */ | |
| prvTraceError("Attempt to free more handles than allocated!"); | |
| } | |
| else | |
| { | |
| objectHandleStacks.indexOfNextAvailableHandle[objectclass]--; | |
| indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass]; | |
| objectHandleStacks.objectHandles[indexOfHandle] = handle; | |
| } | |
| } | |
| /******************************************************************************* | |
| * prvMarkObjectAsUsed | |
| * | |
| * Sets an "is used flag" on object creation, using the first byte of the name | |
| * field. This allows for counting the number of used Object Table slots, even | |
| * if no names have been set. | |
| ******************************************************************************/ | |
| void prvMarkObjectAsUsed(traceObjectClass objectclass, traceHandle handle) | |
| { | |
| uint16_t idx = uiIndexOfObject(handle, objectclass); | |
| RecorderDataPtr->ObjectPropertyTable.objbytes[idx] = 1; | |
| } | |
| /******************************************************************************* | |
| * prvStrncpy | |
| * | |
| * Private string copy function, to improve portability between compilers. | |
| ******************************************************************************/ | |
| static void prvStrncpy(char* dst, const char* src, uint32_t maxLength) | |
| { | |
| uint32_t i; | |
| for (i = 0; i < maxLength; i++) | |
| { | |
| dst[i] = src[i]; | |
| if (src[i] == 0) | |
| break; | |
| } | |
| } | |
| /******************************************************************************* | |
| * prvTraceSetObjectName | |
| * | |
| * Registers the names of queues, semaphores and other kernel objects in the | |
| * recorder's Object Property Table, at the given handle and object class. | |
| ******************************************************************************/ | |
| void prvTraceSetObjectName(traceObjectClass objectclass, | |
| traceHandle handle, | |
| const char* name) | |
| { | |
| static uint16_t idx; | |
| TRACE_ASSERT(name != NULL, "prvTraceSetObjectName: name == NULL", TRC_UNUSED); | |
| if (objectclass >= TRACE_NCLASSES) | |
| { | |
| prvTraceError("Illegal object class in prvTraceSetObjectName"); | |
| return; | |
| } | |
| if (handle == 0) | |
| { | |
| prvTraceError("Illegal handle (0) in prvTraceSetObjectName."); | |
| return; | |
| } | |
| if (handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]) | |
| { | |
| /* ERROR */ | |
| prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass)); | |
| } | |
| else | |
| { | |
| idx = uiIndexOfObject(handle, objectclass); | |
| if (traceErrorMessage == NULL) | |
| { | |
| prvStrncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]), | |
| name, | |
| RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ]); | |
| } | |
| } | |
| } | |
| traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel) | |
| { | |
| uint16_t result; | |
| uint8_t len; | |
| uint8_t crc; | |
| TRACE_ALLOC_CRITICAL_SECTION(); | |
| len = 0; | |
| crc = 0; | |
| TRACE_ASSERT(name != NULL, "prvTraceOpenSymbol: name == NULL", (traceString)0); | |
| prvTraceGetChecksum(name, &crc, &len); | |
| trcCRITICAL_SECTION_BEGIN(); | |
| result = prvTraceLookupSymbolTableEntry(name, crc, len, userEventChannel); | |
| if (!result) | |
| { | |
| result = prvTraceCreateSymbolTableEntry(name, crc, len, userEventChannel); | |
| } | |
| trcCRITICAL_SECTION_END(); | |
| return result; | |
| } | |
| /****************************************************************************** | |
| * vTraceSetFrequency | |
| * | |
| * Registers the clock rate of the time source for the event timestamping. | |
| * This is normally not required, but if the default value (TRC_HWTC_FREQ_HZ) | |
| * should be incorrect for your setup, you can override it using this function. | |
| * | |
| * Must be called prior to vTraceEnable, and the time source is assumed to | |
| * have a fixed clock frequency after the startup. | |
| * | |
| * Note that, in snapshot mode, the value is divided by the TRC_HWTC_DIVISOR. | |
| * This is a software "prescaler" that is also applied on the timestamps. | |
| *****************************************************************************/ | |
| void vTraceSetFrequency(uint32_t frequency) | |
| { | |
| timestampFrequency = frequency; | |
| } | |
| /******************************************************************************* | |
| * Supporting functions | |
| ******************************************************************************/ | |
| /******************************************************************************* | |
| * prvTraceError | |
| * | |
| * Called by various parts in the recorder. Stops the recorder and stores a | |
| * pointer to an error message, which is printed by the monitor task. | |
| * If you are not using the monitor task, you may use xTraceGetLastError() | |
| * from your application to check if the recorder is OK. | |
| * | |
| * Note: If a recorder error is registered before vTraceStart is called, the | |
| * trace start will be aborted. This can occur if any of the Nxxxx constants | |
| * (e.g., TRC_CFG_NTASK) in trcConfig.h is too small. | |
| ******************************************************************************/ | |
| void prvTraceError(const char* msg) | |
| { | |
| /* Stop the recorder */ | |
| if (RecorderDataPtr != NULL) | |
| { | |
| vTraceStop(); | |
| } | |
| /* If first error only... */ | |
| if (traceErrorMessage == NULL) | |
| { | |
| traceErrorMessage = (char*)(intptr_t) msg; | |
| if (RecorderDataPtr != NULL) | |
| { | |
| prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80); | |
| RecorderDataPtr->internalErrorOccured = 1; | |
| } | |
| } | |
| } | |
| void vTraceSetFilterMask(uint16_t filterMask) | |
| { | |
| CurrentFilterMask = filterMask; | |
| } | |
| void vTraceSetFilterGroup(uint16_t filterGroup) | |
| { | |
| CurrentFilterGroup = filterGroup; | |
| } | |
| /****************************************************************************** | |
| * prvCheckDataToBeOverwrittenForMultiEntryEvents | |
| * | |
| * This checks if the next event to be overwritten is a multi-entry user event, | |
| * i.e., a USER_EVENT followed by data entries. | |
| * Such data entries do not have an event code at byte 0, as other events. | |
| * All 4 bytes are user data, so the first byte of such data events must | |
| * not be interpreted as type field. The number of data entries following | |
| * a USER_EVENT is given in the event code of the USER_EVENT. | |
| * Therefore, when overwriting a USER_EVENT (when using in ring-buffer mode) | |
| * any data entries following must be replaced with NULL events (code 0). | |
| * | |
| * This is assumed to execute within a critical section... | |
| *****************************************************************************/ | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nofEntriesToCheck) | |
| { | |
| /* Generic "int" type is desired - should be 16 bit variable on 16 bit HW */ | |
| unsigned int i = 0; | |
| unsigned int e = 0; | |
| TRACE_ASSERT(nofEntriesToCheck != 0, | |
| "prvCheckDataToBeOverwrittenForMultiEntryEvents: nofEntriesToCheck == 0", TRC_UNUSED); | |
| while (i < nofEntriesToCheck) | |
| { | |
| e = RecorderDataPtr->nextFreeIndex + i; | |
| if ((RecorderDataPtr->eventData[e*4] > USER_EVENT) && | |
| (RecorderDataPtr->eventData[e*4] < USER_EVENT + 16)) | |
| { | |
| uint8_t nDataEvents = (uint8_t)(RecorderDataPtr->eventData[e*4] - USER_EVENT); | |
| if ((e + nDataEvents) < RecorderDataPtr->maxEvents) | |
| { | |
| (void)memset(& RecorderDataPtr->eventData[e*4], 0, (size_t) (4 + 4 * nDataEvents)); | |
| } | |
| } | |
| else if (RecorderDataPtr->eventData[e*4] == DIV_XPS) | |
| { | |
| if ((e + 1) < RecorderDataPtr->maxEvents) | |
| { | |
| /* Clear 8 bytes */ | |
| (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4 + 4); | |
| } | |
| else | |
| { | |
| /* Clear 8 bytes, 4 first and 4 last */ | |
| (void)memset(& RecorderDataPtr->eventData[0], 0, 4); | |
| (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4); | |
| } | |
| } | |
| i++; | |
| } | |
| } | |
| #endif | |
| /******************************************************************************* | |
| * prvTraceUpdateCounters | |
| * | |
| * Updates the index of the event buffer. | |
| ******************************************************************************/ | |
| void prvTraceUpdateCounters(void) | |
| { | |
| if (RecorderDataPtr->recorderActive == 0) | |
| { | |
| return; | |
| } | |
| RecorderDataPtr->numEvents++; | |
| RecorderDataPtr->nextFreeIndex++; | |
| if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE)) | |
| { | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| RecorderDataPtr->bufferIsFull = 1; | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| #else | |
| vTraceStop(); | |
| #endif | |
| } | |
| #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER) | |
| prvCheckDataToBeOverwrittenForMultiEntryEvents(1); | |
| #endif | |
| } | |
| /****************************************************************************** | |
| * prvTraceGetDTS | |
| * | |
| * Returns a differential timestamp (DTS), i.e., the time since | |
| * last event, and creates an XTS event if the DTS does not fit in the | |
| * number of bits given. The XTS event holds the MSB bytes of the DTS. | |
| * | |
| * The parameter param_maxDTS should be 0xFF for 8-bit dts or 0xFFFF for | |
| * events with 16-bit dts fields. | |
| *****************************************************************************/ | |
| uint16_t prvTraceGetDTS(uint16_t param_maxDTS) | |
| { | |
| static uint32_t old_timestamp = 0; | |
| XTSEvent* xts = 0; | |
| uint32_t dts = 0; | |
| uint32_t timestamp = 0; | |
| TRACE_ASSERT(param_maxDTS == 0xFF || param_maxDTS == 0xFFFF, "prvTraceGetDTS: Invalid value for param_maxDTS", 0); | |
| if (RecorderDataPtr->frequency == 0) | |
| { | |
| if (timestampFrequency != 0) | |
| { | |
| /* If to override default TRC_HWTC_FREQ_HZ value with value set by vTraceSetFrequency */ | |
| RecorderDataPtr->frequency = timestampFrequency / (TRC_HWTC_DIVISOR); | |
| } | |
| else if (init_hwtc_count != (TRC_HWTC_COUNT)) | |
| { | |
| /* If using default value and timer has been started. | |
| Note: If the default frequency value set here would be incorrect, e.g., | |
| if the timer has actually not been configured yet, override this | |
| with vTraceSetFrequency. | |
| */ | |
| RecorderDataPtr->frequency = (TRC_HWTC_FREQ_HZ) / (TRC_HWTC_DIVISOR); | |
| } | |
| /* If no override (vTraceSetFrequency) and timer inactive -> no action */ | |
| } | |
| /************************************************************************** | |
| * The below statements read the timestamp from the timer port module. | |
| * If necessary, whole seconds are extracted using division while the rest | |
| * comes from the modulo operation. | |
| **************************************************************************/ | |
| prvTracePortGetTimeStamp(×tamp); | |
| /*************************************************************************** | |
| * Since dts is unsigned the result will be correct even if timestamp has | |
| * wrapped around. | |
| ***************************************************************************/ | |
| dts = timestamp - old_timestamp; | |
| old_timestamp = timestamp; | |
| if (RecorderDataPtr->frequency > 0) | |
| { | |
| /* Check if dts > 1 second */ | |
| if (dts > RecorderDataPtr->frequency) | |
| { | |
| /* More than 1 second has passed */ | |
| RecorderDataPtr->absTimeLastEventSecond += dts / RecorderDataPtr->frequency; | |
| /* The part that is not an entire second is added to absTimeLastEvent */ | |
| RecorderDataPtr->absTimeLastEvent += dts % RecorderDataPtr->frequency; | |
| } | |
| else | |
| { | |
| RecorderDataPtr->absTimeLastEvent += dts; | |
| } | |
| /* Check if absTimeLastEvent >= 1 second */ | |
| if (RecorderDataPtr->absTimeLastEvent >= RecorderDataPtr->frequency) | |
| { | |
| /* RecorderDataPtr->absTimeLastEvent is more than or equal to 1 second, but always less than 2 seconds */ | |
| RecorderDataPtr->absTimeLastEventSecond++; | |
| RecorderDataPtr->absTimeLastEvent -= RecorderDataPtr->frequency; | |
| /* RecorderDataPtr->absTimeLastEvent is now less than 1 second */ | |
| } | |
| } | |
| else | |
| { | |
| /* Special case if the recorder has not yet started (frequency may be uninitialized, i.e., zero) */ | |
| RecorderDataPtr->absTimeLastEvent = timestamp; | |
| } | |
| /* If the dts (time since last event) does not fit in event->dts (only 8 or 16 bits) */ | |
| if (dts > param_maxDTS) | |
| { | |
| /* Create an XTS event (eXtended TimeStamp) containing the higher dts bits*/ | |
| xts = (XTSEvent*) prvTraceNextFreeEventBufferSlot(); | |
| if (xts != NULL) | |
| { | |
| if (param_maxDTS == 0xFFFF) | |
| { | |
| xts->type = XTS16; | |
| xts->xts_16 = (uint16_t)((dts / 0x10000) & 0xFFFF); | |
| xts->xts_8 = 0; | |
| } | |
| else if (param_maxDTS == 0xFF) | |
| { | |
| xts->type = XTS8; | |
| xts->xts_16 = (uint16_t)((dts / 0x100) & 0xFFFF); | |
| xts->xts_8 = (uint8_t)((dts / 0x1000000) & 0xFF); | |
| } | |
| else | |
| { | |
| prvTraceError("Bad param_maxDTS in prvTraceGetDTS"); | |
| } | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| return (uint16_t)dts & param_maxDTS; | |
| } | |
| /******************************************************************************* | |
| * prvTraceLookupSymbolTableEntry | |
| * | |
| * Find an entry in the symbol table, return 0 if not present. | |
| * | |
| * The strings are stored in a byte pool, with four bytes of "meta-data" for | |
| * every string. | |
| * byte 0-1: index of next entry with same checksum (for fast lookup). | |
| * byte 2-3: reference to a symbol table entry, a label for vTracePrintF | |
| * format strings only (the handle of the destination channel). | |
| * byte 4..(4 + length): the string (object name or user event label), with | |
| * zero-termination | |
| ******************************************************************************/ | |
| traceString prvTraceLookupSymbolTableEntry(const char* name, | |
| uint8_t crc6, | |
| uint8_t len, | |
| traceString chn) | |
| { | |
| uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ]; | |
| TRACE_ASSERT(name != NULL, "prvTraceLookupSymbolTableEntry: name == NULL", (traceString)0); | |
| TRACE_ASSERT(len != 0, "prvTraceLookupSymbolTableEntry: len == 0", (traceString)0); | |
| while (i != 0) | |
| { | |
| if (RecorderDataPtr->SymbolTable.symbytes[i + 2] == (chn & 0x00FF)) | |
| { | |
| if (RecorderDataPtr->SymbolTable.symbytes[i + 3] == (chn / 0x100)) | |
| { | |
| if (RecorderDataPtr->SymbolTable.symbytes[i + 4 + len] == '\0') | |
| { | |
| if (strncmp((char*)(& RecorderDataPtr->SymbolTable.symbytes[i + 4]), name, len) == 0) | |
| { | |
| break; /* found */ | |
| } | |
| } | |
| } | |
| } | |
| i = (uint16_t)(RecorderDataPtr->SymbolTable.symbytes[i] + (RecorderDataPtr->SymbolTable.symbytes[i + 1] * 0x100)); | |
| } | |
| return i; | |
| } | |
| /******************************************************************************* | |
| * prvTraceCreateSymbolTableEntry | |
| * | |
| * Creates an entry in the symbol table, independent if it exists already. | |
| * | |
| * The strings are stored in a byte pool, with four bytes of "meta-data" for | |
| * every string. | |
| * byte 0-1: index of next entry with same checksum (for fast lookup). | |
| * byte 2-3: reference to a symbol table entry, a label for vTracePrintF | |
| * format strings only (the handle of the destination channel). | |
| * byte 4..(4 + length): the string (object name or user event label), with | |
| * zero-termination | |
| ******************************************************************************/ | |
| uint16_t prvTraceCreateSymbolTableEntry(const char* name, | |
| uint8_t crc6, | |
| uint8_t len, | |
| traceString channel) | |
| { | |
| uint16_t ret = 0; | |
| TRACE_ASSERT(name != NULL, "prvTraceCreateSymbolTableEntry: name == NULL", 0); | |
| TRACE_ASSERT(len != 0, "prvTraceCreateSymbolTableEntry: len == 0", 0); | |
| if (RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + len + 4 >= (TRC_CFG_SYMBOL_TABLE_SIZE)) | |
| { | |
| prvTraceError("Symbol table full. Increase TRC_CFG_SYMBOL_TABLE_SIZE in trcConfig.h"); | |
| ret = 0; | |
| } | |
| else | |
| { | |
| RecorderDataPtr->SymbolTable.symbytes | |
| [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex] = | |
| (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] & 0x00FF); | |
| RecorderDataPtr->SymbolTable.symbytes | |
| [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 1] = | |
| (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] / 0x100); | |
| RecorderDataPtr->SymbolTable.symbytes | |
| [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 2] = | |
| (uint8_t)(channel & 0x00FF); | |
| RecorderDataPtr->SymbolTable.symbytes | |
| [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 3] = | |
| (uint8_t)(channel / 0x100); | |
| /* set name (bytes 4...4+len-1) */ | |
| prvStrncpy((char*)&(RecorderDataPtr->SymbolTable.symbytes | |
| [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4]), name, len); | |
| /* Set zero termination (at offset 4+len) */ | |
| RecorderDataPtr->SymbolTable.symbytes | |
| [RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 + len] = '\0'; | |
| /* store index of entry (for return value, and as head of LL[crc6]) */ | |
| RecorderDataPtr->SymbolTable.latestEntryOfChecksum | |
| [ crc6 ] = (uint16_t)RecorderDataPtr->SymbolTable.nextFreeSymbolIndex; | |
| RecorderDataPtr->SymbolTable.nextFreeSymbolIndex += (uint32_t) (len + 5); | |
| ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - (uint8_t)(len + 5)); | |
| } | |
| return ret; | |
| } | |
| /******************************************************************************* | |
| * prvTraceGetChecksum | |
| * | |
| * Calculates a simple 6-bit checksum from a string, used to index the string | |
| * for fast symbol table lookup. | |
| ******************************************************************************/ | |
| void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength) | |
| { | |
| unsigned char c; | |
| int length = 1; /* Should be 1 to account for '\0' */ | |
| int crc = 0; | |
| TRACE_ASSERT(pname != NULL, "prvTraceGetChecksum: pname == NULL", TRC_UNUSED); | |
| TRACE_ASSERT(pcrc != NULL, "prvTraceGetChecksum: pcrc == NULL", TRC_UNUSED); | |
| TRACE_ASSERT(plength != NULL, "prvTraceGetChecksum: plength == NULL", TRC_UNUSED); | |
| if (pname != (const char *) 0) | |
| { | |
| for (; (c = (unsigned char) *pname++) != '\0';) | |
| { | |
| crc += c; | |
| length++; | |
| } | |
| } | |
| *pcrc = (uint8_t)(crc & 0x3F); | |
| *plength = (uint8_t)length; | |
| } | |
| #if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1) | |
| static void prvTraceStoreXID(traceHandle handle); | |
| /****************************************************************************** | |
| * prvTraceStoreXID | |
| * | |
| * Stores an XID (eXtended IDentifier) event. | |
| * This is used if an object/task handle is larger than 255. | |
| * The parameter "handle" is the full (16 bit) handle, assumed to be 256 or | |
| * larger. Handles below 256 should not use this function. | |
| * | |
| * NOTE: this function MUST be called from within a critical section. | |
| *****************************************************************************/ | |
| static void prvTraceStoreXID(traceHandle handle) | |
| { | |
| XPSEvent* xid; | |
| TRACE_ASSERT(handle >= 256, "prvTraceStoreXID: Handle < 256", TRC_UNUSED); | |
| xid = (XPSEvent*)prvTraceNextFreeEventBufferSlot(); | |
| if (xid != NULL) | |
| { | |
| xid->type = XID; | |
| /* This function is (only) used when traceHandle is 16 bit... */ | |
| xid->xps_16 = handle; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| static uint8_t prvTraceGet8BitHandle(traceHandle handle) | |
| { | |
| if (handle > 255) | |
| { | |
| prvTraceStoreXID(handle); | |
| /* The full handle (16 bit) is stored in the XID event. | |
| This code (255) is used instead of zero (which is an error code).*/ | |
| return 255; | |
| } | |
| return (uint8_t)(handle & 0xFF); | |
| } | |
| #endif /*(TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)*/ | |
| /* If using DWT timestamping (default on ARM Cortex-M3, M4 and M7), make sure the DWT unit is initialized. */ | |
| #ifndef TRC_CFG_ARM_CM_USE_SYSTICK | |
| #if ((TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M) && (defined (__CORTEX_M) && (__CORTEX_M >= 0x03))) | |
| void prvTraceInitCortexM() | |
| { | |
| /* Ensure that the DWT registers are unlocked and can be modified. */ | |
| TRC_REG_ITM_LOCKACCESS = TRC_ITM_LOCKACCESS_UNLOCK; | |
| /* Make sure DWT is enabled, if supported */ | |
| TRC_REG_DEMCR |= TRC_DEMCR_TRCENA; | |
| do{ | |
| /* Verify that DWT is supported */ | |
| if (TRC_REG_DEMCR == 0) | |
| { | |
| /* This function is called on Cortex-M3, M4 and M7 devices to initialize | |
| the DWT unit, assumed present. The DWT cycle counter is used for timestamping. | |
| If the below error is produced, the DWT unit does not seem to be available. | |
| In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build | |
| to use SysTick timestamping instead, or define your own timestamping by | |
| setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED | |
| and make the necessary definitions, as explained in trcHardwarePort.h.*/ | |
| prvTraceError("DWT unit not available, see code comment."); | |
| break; | |
| } | |
| /* Verify that DWT_CYCCNT is supported */ | |
| if (TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT) | |
| { | |
| /* This function is called on Cortex-M3, M4 and M7 devices to initialize | |
| the DWT unit, assumed present. The DWT cycle counter is used for timestamping. | |
| If the below error is produced, the cycle counter does not seem to be available. | |
| In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build | |
| to use SysTick timestamping instead, or define your own timestamping by | |
| setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED | |
| and make the necessary definitions, as explained in trcHardwarePort.h.*/ | |
| prvTraceError("DWT_CYCCNT not available, see code comment."); | |
| break; | |
| } | |
| /* Reset the cycle counter */ | |
| TRC_REG_DWT_CYCCNT = 0; | |
| /* Enable the cycle counter */ | |
| TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA; | |
| }while(0); /* breaks above jump here */ | |
| } | |
| #endif | |
| #endif | |
| /****************************************************************************** | |
| * prvTracePortGetTimeStamp | |
| * | |
| * Returns the current time based on the HWTC macros which provide a hardware | |
| * isolation layer towards the hardware timer/counter. | |
| * | |
| * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue | |
| * or the trace recorder library. Typically you should not need to change | |
| * the code of prvTracePortGetTimeStamp if using the HWTC macros. | |
| * | |
| ******************************************************************************/ | |
| void prvTracePortGetTimeStamp(uint32_t *pTimestamp) | |
| { | |
| static uint32_t last_hwtc_count = 0; | |
| uint32_t hwtc_count = 0; | |
| #if TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR | |
| /* systick based timer */ | |
| static uint32_t last_traceTickCount = 0; | |
| uint32_t traceTickCount = 0; | |
| #else /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/ | |
| /* Free running timer */ | |
| static uint32_t last_hwtc_rest = 0; | |
| uint32_t diff = 0; | |
| uint32_t diff_scaled = 0; | |
| #endif /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/ | |
| if (trace_disable_timestamp == 1) | |
| { | |
| if (pTimestamp) | |
| *pTimestamp = last_timestamp; | |
| return; | |
| } | |
| /* Retrieve TRC_HWTC_COUNT only once since the same value should be used all throughout this function. */ | |
| #if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR) | |
| /* Get the increasing tick count */ | |
| hwtc_count = (TRC_HWTC_COUNT); | |
| #elif (TRC_HWTC_TYPE == TRC_OS_TIMER_DECR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR) | |
| /* Convert decreasing tick count into increasing tick count */ | |
| hwtc_count = (TRC_HWTC_PERIOD) - (TRC_HWTC_COUNT); | |
| #else | |
| #error "TRC_HWTC_TYPE has unexpected value" | |
| #endif | |
| #if (TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_Win32) | |
| /* The Win32 port uses ulGetRunTimeCounterValue for timestamping, which in turn | |
| uses QueryPerformanceCounter. That function is not always reliable when used over | |
| multiple threads. We must therefore handle rare cases where the timestamp is less | |
| than the previous. In practice, this should "never" roll over since the | |
| performance counter is 64 bit wide. */ | |
| if (last_hwtc_count > hwtc_count) | |
| { | |
| hwtc_count = last_hwtc_count; | |
| } | |
| #endif | |
| #if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR) | |
| /* Timestamping is based on a timer that wraps at TRC_HWTC_PERIOD */ | |
| if (last_traceTickCount - uiTraceTickCount - 1 < 0x80000000) | |
| { | |
| /* This means last_traceTickCount is higher than uiTraceTickCount, | |
| so we have previously compensated for a missed tick. | |
| Therefore we use the last stored value because that is more accurate. */ | |
| traceTickCount = last_traceTickCount; | |
| } | |
| else | |
| { | |
| /* Business as usual */ | |
| traceTickCount = uiTraceTickCount; | |
| } | |
| /* Check for overflow. May occur if the update of uiTraceTickCount has been | |
| delayed due to disabled interrupts. */ | |
| if (traceTickCount == last_traceTickCount && hwtc_count < last_hwtc_count) | |
| { | |
| /* A trace tick has occurred but not been executed by the kernel, so we compensate manually. */ | |
| traceTickCount++; | |
| } | |
| /* Check if the return address is OK, then we perform the calculation. */ | |
| if (pTimestamp) | |
| { | |
| /* Get timestamp from trace ticks. Scale down the period to avoid unwanted overflows. */ | |
| last_timestamp = traceTickCount * ((TRC_HWTC_PERIOD) / (TRC_HWTC_DIVISOR)); | |
| /* Increase timestamp by (hwtc_count + "lost hardware ticks from scaling down period") / TRC_HWTC_DIVISOR. */ | |
| last_timestamp += (hwtc_count + traceTickCount * ((TRC_HWTC_PERIOD) % (TRC_HWTC_DIVISOR))) / (TRC_HWTC_DIVISOR); | |
| } | |
| /* Store the previous value */ | |
| last_traceTickCount = traceTickCount; | |
| #else /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/ | |
| /* Timestamping is based on a free running timer */ | |
| /* This part handles free running clocks that can be scaled down to avoid too large DTS values. | |
| Without this, the scaled timestamp will incorrectly wrap at (2^32 / TRC_HWTC_DIVISOR) ticks. | |
| The scaled timestamp returned from this function is supposed to go from 0 -> 2^32, which in real time would represent (0 -> 2^32 * TRC_HWTC_DIVISOR) ticks. */ | |
| /* First we see how long time has passed since the last timestamp call, and we also add the ticks that was lost when we scaled down the last time. */ | |
| diff = (hwtc_count - last_hwtc_count) + last_hwtc_rest; | |
| /* Scale down the diff */ | |
| diff_scaled = diff / (TRC_HWTC_DIVISOR); | |
| /* Find out how many ticks were lost when scaling down, so we can add them the next time */ | |
| last_hwtc_rest = diff % (TRC_HWTC_DIVISOR); | |
| /* We increase the scaled timestamp by the scaled amount */ | |
| last_timestamp += diff_scaled; | |
| #endif /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/ | |
| /* Is anyone interested in the results? */ | |
| if (pTimestamp) | |
| *pTimestamp = last_timestamp; | |
| /* Store the previous value */ | |
| last_hwtc_count = hwtc_count; | |
| } | |
| #endif /*(TRC_USE_TRACEALYZER_RECORDER == 1)*/ | |
| #endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)*/ |