/* * Trace Recorder for Tracealyzer v4.8.1 * Copyright 2023 Percepio AB * www.percepio.com * * SPDX-License-Identifier: Apache-2.0 * * The generic core of the trace recorder's streaming mode. */ #include #if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING) #if (TRC_USE_TRACEALYZER_RECORDER == 1) #ifndef TRC_KERNEL_PORT_HEAP_INIT #define TRC_KERNEL_PORT_HEAP_INIT(__size) #endif /* Entry symbol length maximum check */ #if ((TRC_CFG_ENTRY_SYMBOL_MAX_LENGTH) > 28UL) #error Maximum entry symbol length is 28! #endif /* Entry symbol length minimum check */ #if ((TRC_CFG_ENTRY_SYMBOL_MAX_LENGTH) < 4UL) #error Minimum entry symbol length is 4! #endif typedef struct TraceHeader { uint32_t uiPSF; uint16_t uiVersion; uint16_t uiPlatform; uint32_t uiOptions; uint32_t uiNumCores; uint32_t isrTailchainingThreshold; uint16_t uiPlatformCfgPatch; uint8_t uiPlatformCfgMinor; uint8_t uiPlatformCfgMajor; char platformCfg[8]; } TraceHeader_t; /* The data structure for commands (a bit overkill) */ typedef struct TraceCommandType_t { unsigned char cmdCode; unsigned char param1; unsigned char param2; unsigned char param3; unsigned char param4; unsigned char param5; unsigned char checksumLSB; unsigned char checksumMSB; } TraceCommand_t; #ifndef TRC_CFG_RECORDER_DATA_INIT #define TRC_CFG_RECORDER_DATA_INIT 1 #endif /* Used to interpret the data format */ #define TRACE_FORMAT_VERSION ((uint16_t)0x000E) /* Used to determine endian of data (big/little) */ #define TRACE_PSF_ENDIANESS_IDENTIFIER ((uint32_t)0x50534600) #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC) static TraceRecorderData_t xRecorderData TRC_CFG_RECORDER_DATA_ATTRIBUTE; /*cstat !MISRAC2004-8.7 !MISRAC2012-Rule-8.9_a !MISRAC2012-Rule-8.9_b Suppress global variable check*/ TraceRecorderData_t* pxTraceRecorderData TRC_CFG_RECORDER_DATA_ATTRIBUTE; #else /* If using DYNAMIC or CUSTOM allocation */ TraceRecorderData_t* pxTraceRecorderData TRC_CFG_RECORDER_DATA_ATTRIBUTE; #endif static TraceHeader_t* pxHeader TRC_CFG_RECORDER_DATA_ATTRIBUTE; /*cstat !MISRAC2004-8.7 !MISRAC2012-Rule-8.9_a !MISRAC2012-Rule-8.9_b Suppress global variable check*/ /******************************************************************************* * RecorderInitialized * * Makes sure the recorder data is only initialized once. * * NOTE: RecorderInitialized is only initialized to 0 if * TRC_CFG_RECORDER_DATA_INIT is non-zero. * This will avoid issues where the recorder must be started before main(), * which can lead to RecorderInitialized be cleared by late initialization after * xTraceEnable(TRC_INIT) was called and assigned RecorderInitialized its' * value. ******************************************************************************/ #if (TRC_CFG_RECORDER_DATA_INIT != 0) uint32_t RecorderInitialized = 0u; #else /* (TRC_CFG_RECORDER_DATA_INIT != 0) */ uint32_t RecorderInitialized TRC_CFG_RECORDER_DATA_ATTRIBUTE; #endif /* (TRC_CFG_RECORDER_DATA_INIT != 0) */ #if (TRC_EXTERNAL_BUFFERS == 0) /* Stores the header information on Start */ static void prvTraceStoreHeader(void); /* Store the Timestamp info */ static void prvTraceStoreTimestampInfo(void); /* Stores the entry table on Start */ static void prvTraceStoreEntryTable(void); #else /* (TRC_EXTERNAL_BUFFERS == 0) */ #define prvTraceStoreHeader() #define prvTraceStoreTimestampInfo() #define prvTraceStoreEntryTable() #endif /* (TRC_EXTERNAL_BUFFERS == 0) */ /* Store start event. */ static void prvTraceStoreStartEvent(void); /* Checks if the provided command is a valid command */ static int32_t prvIsValidCommand(const TraceCommand_t* const cmd); /* Executed the received command (Start or Stop) */ static void prvProcessCommand(const TraceCommand_t* const cmd); /* Internal function for starting the recorder */ static void prvSetRecorderEnabled(void); /* Internal function for stopping the recorder */ static void prvSetRecorderDisabled(void); static TraceUnsignedBaseType_t prvIs64bit(void); /****************************************************************************** * xTraceInitialize * * Initializes the recorder data. * This function will be called by xTraceEnable(...). * Only needs to be called manually if traced objects are created before the * trace recorder can be enabled, at which point make sure to call this function * as early as possible. * See TRC_CFG_RECORDER_DATA_INIT in trcConfig.h. ******************************************************************************/ traceResult xTraceInitialize(void) { TraceUnsignedBaseType_t i; TRC_ASSERT_EQUAL_SIZE(TraceUnsignedBaseType_t, TraceBaseType_t); /* TraceUnsignedBaseType_t is used to store handles (addresses) */ TRC_ASSERT_EQUAL_SIZE(TraceUnsignedBaseType_t, TraceHandleBaseType_t); if (RecorderInitialized != 0u) { return TRC_SUCCESS; } TRC_PORT_SPECIFIC_INIT(); #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC) pxTraceRecorderData = &xRecorderData; #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_DYNAMIC) /* Initialize heap */ TRC_KERNEL_PORT_HEAP_INIT(sizeof(TraceRecorderData_t)); /* Allocate data */ pxTraceRecorderData = TRC_KERNEL_PORT_HEAP_MALLOC(sizeof(TraceRecorderData_t)); #endif /* These are set on init so they aren't overwritten by late initialization values. */ pxTraceRecorderData->uiSessionCounter = 0u; pxTraceRecorderData->uiRecorderEnabled = 0u; for (i = 0; i < TRC_CFG_CORE_COUNT; i++) { pxTraceRecorderData->uxTraceSystemStates[i] = (TraceUnsignedBaseType_t)TRC_STATE_IN_STARTUP; } /*cstat !MISRAC2004-13.7_b Suppress always false check*/ if (xTraceEntryIndexTableInitialize(&pxTraceRecorderData->xEntryIndexTableBuffer) == TRC_FAIL) { return TRC_FAIL; } #if (TRC_EXTERNAL_BUFFERS == 0) if (xTraceHeaderInitialize(&pxTraceRecorderData->xHeaderBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceEntryTableInitialize(&pxTraceRecorderData->xEntryTable) == TRC_FAIL) { return TRC_FAIL; } if (xTraceTimestampInitialize(&pxTraceRecorderData->xTimestampBuffer) == TRC_FAIL) { return TRC_FAIL; } #endif if (xTraceCounterInitialize(&pxTraceRecorderData->xCounterBuffer) == TRC_FAIL) { return TRC_FAIL; } /*cstat !MISRAC2004-13.7_b !MISRAC2012-Rule-14.3_b Suppress always false check*/ if (xTraceStackMonitorInitialize(&pxTraceRecorderData->xStackMonitorBuffer) == TRC_FAIL) { return TRC_FAIL; } /*cstat !MISRAC2004-13.7_b !MISRAC2012-Rule-14.3_b Suppress always false check*/ if (xTraceStreamPortInitialize(&pxTraceRecorderData->xStreamPortBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceAssertInitialize(&pxTraceRecorderData->xAssertBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceDiagnosticsInitialize(&pxTraceRecorderData->xDiagnosticsBuffer) == TRC_FAIL) { return TRC_FAIL; } /*cstat !MISRAC2004-13.7_b Suppress always false check*/ if (xTraceExtensionInitialize(&pxTraceRecorderData->xExtensionBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceStaticBufferInitialize(&pxTraceRecorderData->xStaticBufferBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceEventInitialize(&pxTraceRecorderData->xEventDataBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTracePrintInitialize(&pxTraceRecorderData->xPrintBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceErrorInitialize(&pxTraceRecorderData->xErrorBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceISRInitialize(&pxTraceRecorderData->xISRBuffer) == TRC_FAIL) { return TRC_FAIL; } if (xTraceTaskInitialize(&pxTraceRecorderData->xTaskInfoBuffer) == TRC_FAIL) { return TRC_FAIL; } /*cstat !MISRAC2004-13.7_b !MISRAC2012-Rule-14.3_b Suppress always false check*/ if (xTraceKernelPortInitialize(&pxTraceRecorderData->xKernelPortBuffer) == TRC_FAIL) { return TRC_FAIL; } pxTraceRecorderData->reserved = 0xFFFFFFFFUL; xTraceSetComponentInitialized(TRC_RECORDER_COMPONENT_CORE); return TRC_SUCCESS; } /* Do this in function to avoid unreachable code warnings */ traceResult prvVerifySizeAlignment(uint32_t ulSize) { return (ulSize % sizeof(TraceUnsignedBaseType_t)) == 0 ? TRC_SUCCESS : TRC_FAIL; } traceResult xTraceHeaderInitialize(TraceHeaderBuffer_t *pxBuffer) { uint32_t i; const char* platform_cfg = TRC_PLATFORM_CFG; /*cstat !MISRAC2004-6.3 !MISRAC2012-Dir-4.6_a Suppress basic char type usage*/ TRC_ASSERT_EQUAL_SIZE(TraceHeaderBuffer_t, TraceHeader_t); if (pxBuffer == (void*)0) { return TRC_FAIL; } if (prvVerifySizeAlignment(sizeof(TraceStreamPortBuffer_t)) == TRC_FAIL) { /* TraceStreamPortBuffer_t size is not aligned to TraceUnsignedBaseType_t */ return TRC_FAIL; } if (prvVerifySizeAlignment(sizeof(TraceEventDataTable_t)) == TRC_FAIL) { /* TraceEventDataTable_t size is not aligned to TraceUnsignedBaseType_t */ return TRC_FAIL; } if (prvVerifySizeAlignment(sizeof(TraceKernelPortDataBuffer_t)) == TRC_FAIL) { /* TraceKernelPortDataBuffer_t size is not aligned to TraceUnsignedBaseType_t */ return TRC_FAIL; } pxHeader = (TraceHeader_t*)pxBuffer; /*cstat !MISRAC2004-11.4 !MISRAC2012-Rule-11.3 Suppress conversion between pointer types checks*/ pxHeader->uiPSF = TRACE_PSF_ENDIANESS_IDENTIFIER; pxHeader->uiVersion = TRACE_FORMAT_VERSION; pxHeader->uiPlatform = TRACE_KERNEL_VERSION; for (i = 0u; i < (uint32_t)(TRC_PLATFORM_CFG_LENGTH); i++) { pxHeader->platformCfg[i] = platform_cfg[i]; /*cstat !MISRAC2004-17.4_b We need to access every character in the string*/ if (platform_cfg[i] == (char)0) /*cstat !MISRAC2004-6.3 !MISRAC2012-Dir-4.6_a Suppress basic char type usage*/ /*cstat !MISRAC2004-17.4_b We need to access every character in the string*/ { break; } } pxHeader->uiPlatformCfgPatch = (uint16_t)TRC_PLATFORM_CFG_PATCH; pxHeader->uiPlatformCfgMinor = (uint8_t)TRC_PLATFORM_CFG_MINOR; pxHeader->uiPlatformCfgMajor = (uint8_t)TRC_PLATFORM_CFG_MAJOR; pxHeader->uiNumCores = (uint32_t)TRC_CFG_CORE_COUNT; #ifdef TRC_STREAM_PORT_MULTISTREAM_SUPPORT pxHeader->uiNumCores |= 2 << 8; #else pxHeader->uiNumCores |= 3 << 8; #endif pxHeader->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD; /* Lowest bit used for TRC_IRQ_PRIORITY_ORDER */ pxHeader->uiOptions = (((uint32_t)(TRC_IRQ_PRIORITY_ORDER)) << 0); /* 3rd bit used for TRC_CFG_TEST_MODE */ pxHeader->uiOptions |= (((uint32_t)(TRC_CFG_TEST_MODE)) << 2); /* 4th bit used for 64-bit*/ if (prvIs64bit()) /* Call helper function to avoid "unreachable code" */ { pxHeader->uiOptions |= (1 << 3); } return TRC_SUCCESS; } traceResult xTraceEnable(uint32_t uiStartOption) { TraceCommand_t xCommand = { 0 }; int32_t iBytes; if (xTraceInitialize() == TRC_FAIL) { return TRC_FAIL; } /*cstat !MISRAC2004-13.7_b !MISRAC2012-Rule-14.3_b Suppress always false check*/ if (xTraceStreamPortOnEnable(uiStartOption) == TRC_FAIL) { return TRC_FAIL; } /*cstat !MISRAC2004-13.7_b !MISRAC2012-Rule-14.3_b Suppress always false check*/ if (xTraceKernelPortEnable() == TRC_FAIL) { return TRC_FAIL; } if (uiStartOption == TRC_START_AWAIT_HOST) { /* We keep trying to read commands from host until the recorder has been started */ do { iBytes = 0; if (xTraceStreamPortReadData(&xCommand, sizeof(TraceCommand_t), (int32_t*)&iBytes) == TRC_FAIL) { (void)xTraceWarning(TRC_WARNING_STREAM_PORT_READ); } if ((uint32_t)iBytes == sizeof(TraceCommand_t)) { if (prvIsValidCommand(&xCommand) != 0) { prvProcessCommand(&xCommand); } } } while (pxTraceRecorderData->uiRecorderEnabled == 0u); } else if (uiStartOption == (uint32_t)(TRC_START)) { /* We start streaming directly - this assumes that the host interface is ready! */ xCommand.cmdCode = CMD_SET_ACTIVE; xCommand.param1 = 1u; prvProcessCommand(&xCommand); } else if (uiStartOption == TRC_START_FROM_HOST) { /* We prepare the system to receive commands from host, but let system resume execution until that happens */ } else { return TRC_FAIL; } return TRC_SUCCESS; } traceResult xTraceDisable(void) { prvSetRecorderDisabled(); (void)xTraceStreamPortOnDisable(); return TRC_SUCCESS; } #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM) traceResult xTraceSetBuffer(TraceRecorderData_t* pxBuffer) { if (pxBuffer == 0) { return TRC_FAIL; } pxTraceRecorderData = pxBuffer; return TRC_SUCCESS; } #endif traceResult xTraceGetEventBuffer(void **ppvBuffer, TraceUnsignedBaseType_t *puiSize) { if ((pxTraceRecorderData == (void*)0) || (ppvBuffer == (void*)0) || (puiSize == (void*)0)) { return TRC_FAIL; } /* Returns the xStreamPortBuffer since that is the one containing trace data */ *ppvBuffer = (void*)&pxTraceRecorderData->xStreamPortBuffer; *puiSize = sizeof(pxTraceRecorderData->xStreamPortBuffer); return TRC_SUCCESS; } traceResult xTraceTzCtrl(void) { TraceCommand_t xCommand = { 0 }; int32_t iRxBytes; do { /* Listen for new commands */ iRxBytes = 0; if (xTraceStreamPortReadData(&xCommand, sizeof(TraceCommand_t), &iRxBytes) == TRC_FAIL) { /* The connection has failed, stop tracing */ (void)xTraceDisable(); return TRC_FAIL; } if ((uint32_t)iRxBytes == sizeof(TraceCommand_t)) { if (prvIsValidCommand(&xCommand) != 0) { prvProcessCommand(&xCommand); /* Start or Stop currently... */ } } if (xTraceIsRecorderEnabled()) { xTraceInternalEventBufferTransfer(); } /* If there was data sent or received (bytes != 0), loop around and repeat, if there is more data to send or receive. Otherwise, step out of this loop and sleep for a while. */ } while (iRxBytes > 0); if (xTraceIsRecorderEnabled()) { (void)xTraceDiagnosticsCheckStatus(); (void)xTraceStackMonitorReport(); } return TRC_SUCCESS; } void vTraceSetFilterGroup(uint16_t filterGroup) { (void)filterGroup; } void vTraceSetFilterMask(uint16_t filterMask) { (void)filterMask; } /******************************************************************************/ /*** INTERNAL FUNCTIONS *******************************************************/ /******************************************************************************/ static TraceUnsignedBaseType_t prvIs64bit(void) { return sizeof(TraceUnsignedBaseType_t) == 8; } /* Internal function for starting/stopping the recorder. */ static void prvSetRecorderEnabled(void) { TraceUnsignedBaseType_t uxTimestampFrequency = 0u; uint32_t uiTimestampPeriod = 0u; TRACE_ALLOC_CRITICAL_SECTION(); if (pxTraceRecorderData->uiRecorderEnabled == 1u) { return; } (void)xTraceTimestampGetFrequency(&uxTimestampFrequency); /* If not overridden using xTraceTimestampSetFrequency(...), use default value */ if (uxTimestampFrequency == 0u) { (void)xTraceTimestampSetFrequency((TraceUnsignedBaseType_t)(TRC_HWTC_FREQ_HZ)); } (void)xTraceTimestampGetPeriod(&uiTimestampPeriod); /* If not overridden using xTraceTimestampSetPeriod(...), use default value */ if (uiTimestampPeriod == 0u) { (void)xTraceTimestampSetPeriod((TraceUnsignedBaseType_t)(TRC_HWTC_PERIOD)); } TRACE_ENTER_CRITICAL_SECTION(); /* If the internal event buffer is used, we must clear it */ (void)xTraceInternalEventBufferClear(); (void)xTraceStreamPortOnTraceBegin(); prvTraceStoreHeader(); prvTraceStoreTimestampInfo(); prvTraceStoreEntryTable(); prvTraceStoreStartEvent(); pxTraceRecorderData->uiSessionCounter++; pxTraceRecorderData->uiRecorderEnabled = 1u; TRACE_EXIT_CRITICAL_SECTION(); } static void prvSetRecorderDisabled(void) { TRACE_ALLOC_CRITICAL_SECTION(); if (pxTraceRecorderData->uiRecorderEnabled == 0u) { return; } TRACE_ENTER_CRITICAL_SECTION(); pxTraceRecorderData->uiRecorderEnabled = 0u; (void)xTraceStreamPortOnTraceEnd(); TRACE_EXIT_CRITICAL_SECTION(); } #if (TRC_EXTERNAL_BUFFERS == 0) /* Stores the header information on Start */ static void prvTraceStoreHeader(void) { TraceEventHandle_t xEventHandle; if (xTraceEventBeginRawOfflineBlocking(sizeof(TraceHeader_t), &xEventHandle) == TRC_SUCCESS) { xTraceEventAddData(xEventHandle, (TraceUnsignedBaseType_t*)pxHeader, sizeof(TraceHeader_t) / sizeof(TraceUnsignedBaseType_t)); xTraceEventEndOfflineBlocking(xEventHandle); } } /* Store the Timestamp */ static void prvTraceStoreTimestampInfo(void) { TraceEventHandle_t xEventHandle; if (xTraceEventBeginRawOfflineBlocking(sizeof(TraceTimestampData_t), &xEventHandle) == TRC_SUCCESS) { xTraceEventAddData(xEventHandle, (TraceUnsignedBaseType_t*)&pxTraceRecorderData->xTimestampBuffer, sizeof(TraceTimestampData_t) / sizeof(TraceUnsignedBaseType_t)); xTraceEventEndOfflineBlocking(xEventHandle); } } /* Stores the entry table on Start */ static void prvTraceStoreEntryTable(void) { uint32_t i = 0; TraceEventHandle_t xEventHandle; TraceEntryHandle_t xEntryHandle; uint32_t uiEntryCount; void *pvEntryAddress; (void)xTraceEntryGetCount(&uiEntryCount); if (xTraceEventBeginRawOfflineBlocking(sizeof(TraceUnsignedBaseType_t) + sizeof(TraceUnsignedBaseType_t) + sizeof(TraceUnsignedBaseType_t), &xEventHandle) == TRC_SUCCESS) { (void)xTraceEventAddUnsignedBaseType(xEventHandle, (TraceUnsignedBaseType_t)uiEntryCount); (void)xTraceEventAddUnsignedBaseType(xEventHandle, TRC_ENTRY_TABLE_SLOT_SYMBOL_SIZE); (void)xTraceEventAddUnsignedBaseType(xEventHandle, TRC_ENTRY_TABLE_STATE_COUNT); (void)xTraceEventEndOfflineBlocking(xEventHandle); } for (i = 0; i < (TRC_ENTRY_TABLE_SLOTS); i++) { (void)xTraceEntryGetAtIndex(i, &xEntryHandle); (void)xTraceEntryGetAddress(xEntryHandle, &pvEntryAddress); /* We only send used entry slots */ if (pvEntryAddress != 0) { /* Send entry */ if (xTraceEventBeginRawOfflineBlocking(sizeof(TraceEntry_t), &xEventHandle) == TRC_SUCCESS) { (void)xTraceEventAddData(xEventHandle, (TraceUnsignedBaseType_t*)xEntryHandle, sizeof(TraceEntry_t) / sizeof(TraceUnsignedBaseType_t)); (void)xTraceEventEndOfflineBlocking(xEventHandle); } } } } #endif /* (TRC_EXTERNAL_BUFFERS == 0) */ static void prvTraceStoreStartEvent(void) { TraceEventHandle_t xEventHandle = 0; void* pvCurrentTask = (void*)0; uint32_t i; if (xTraceEventBeginOffline(PSF_EVENT_TRACE_START, sizeof(TraceUnsignedBaseType_t) * (TRC_CFG_CORE_COUNT), &xEventHandle) == TRC_SUCCESS) { for (i = 0; i < (TRC_CFG_CORE_COUNT); i++) { (void)xTraceTaskGetCurrentOnCore(i, &pvCurrentTask); (void)xTraceEventAddUnsignedBaseType(xEventHandle, (TraceUnsignedBaseType_t)pvCurrentTask); /*cstat !MISRAC2004-11.3 !MISRAC2012-Rule-11.4 !MISRAC2012-Rule-11.6 Suppress conversion from pointer to integer check*/ } (void)xTraceEventEndOffline(xEventHandle); } } /* Checks if the provided command is a valid command */ static int32_t prvIsValidCommand(const TraceCommand_t* const cmd) { uint16_t checksum = (uint16_t)0xFFFFU - (uint16_t)(unsigned char)(cmd->cmdCode + /*cstat !MISRAC2004-6.3 !MISRAC2012-Dir-4.6_a Suppress basic char type usage*/ cmd->param1 + cmd->param2 + cmd->param3 + cmd->param4 + cmd->param5); if (cmd->checksumMSB != (unsigned char)(checksum >> 8)) /*cstat !MISRAC2004-6.3 !MISRAC2012-Dir-4.6_a Suppress basic char type usage*/ { return 0; } if (cmd->checksumLSB != (unsigned char)(checksum & 0xFFU)) /*cstat !MISRAC2004-6.3 !MISRAC2012-Dir-4.6_a Suppress basic char type usage*/ { return 0; } if (cmd->cmdCode > (unsigned char)(CMD_LAST_COMMAND)) /*cstat !MISRAC2004-6.3 !MISRAC2012-Dir-4.6_a Suppress basic char type usage*/ { return 0; } return 1; } /* Executed the received command (Start or Stop) */ static void prvProcessCommand(const TraceCommand_t* const cmd) { switch(cmd->cmdCode) { case CMD_SET_ACTIVE: if (cmd->param1 == 1u) { prvSetRecorderEnabled(); } else { prvSetRecorderDisabled(); } break; default: break; } } #endif #endif