/******************************************************************************* * Tracealyzer v2.5.0 Recorder Library * Percepio AB, www.percepio.com * * trcBase.c * * Core functionality of the trace recorder library. * * Terms of Use * This software is copyright Percepio AB. The recorder library is free for * use together with Percepio products. You may distribute the recorder library * in its original form, including modifications in trcHardwarePort.c/.h * given that these modification are clearly marked as your own modifications * and documented in the initial comment section of these source files. * This software is the intellectual property of Percepio AB and may not be * sold or in other ways commercially redistributed without explicit written * permission by Percepio AB. * * Disclaimer * The trace tool and recorder library is being delivered to you AS IS and * Percepio AB makes no warranty as to its use or performance. Percepio AB does * not and cannot warrant the performance or results you may obtain by using the * software or documentation. Percepio AB make no warranties, express or * implied, as to noninfringement of third party rights, merchantability, or * fitness for any particular purpose. In no event will Percepio AB, its * technology partners, or distributors be liable to you for any consequential, * incidental or special damages, including any lost profits or lost savings, * even if a representative of Percepio AB has been advised of the possibility * of such damages, or for any claim by any third party. Some jurisdictions do * not allow the exclusion or limitation of incidental, consequential or special * damages, or the exclusion of implied warranties or limitations on how long an * implied warranty may last, so the above limitations may not apply to you. * * Copyright Percepio AB, 2013. * www.percepio.com ******************************************************************************/ #include "trcBase.h" #if (USE_TRACEALYZER_RECORDER == 1) #include /******************************************************************************* * Static data initializations ******************************************************************************/ /* Structure to handle the exclude flags for all objects and tasks. We add some extra objects since index 0 is not used for each object class. */ uint8_t excludedObjects[(TRACE_KERNEL_OBJECT_COUNT + TRACE_NCLASSES) / 8 + 1] = { 0 }; /* Structure to handle the exclude flags for all event codes */ uint8_t excludedEventCodes[NEventCodes / 8 + 1] = { 0 }; /* Keeps track of available handles */ objectHandleStackType objectHandleStacks = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }; uint32_t init_hwtc_count; /******************************************************************************* * RecorderData * * The main data structure. This is the data read by Tracealyzer, typically * through a debugger RAM dump. The recorder access this through the pointer * RecorderDataPtr, to allow for dynamic memory allocation as well. * * On the NXP LPC17xx you may use the secondary RAM bank (AHB RAM) for this * purpose. For instance, the LPC1766 has 32 KB AHB RAM which allows for * allocating a buffer size of at least 7500 events without affecting the main * RAM. To place RecorderData in this RAM bank, use the below declaration. * * #pragma location="AHB_RAM_MEMORY" * RecorderDataType RecorderData = ... * * This of course works for other hardware architectures with additional RAM * banks as well, just replace "AHB_RAM_MEMORY" with the name of the right * address section from the linker file. * * However, to keep trcBase.c portable and still have a preconfigured IAR demo * using AHB RAM, we don't add the pragma directly in trcBase.c but in a header * included where the pragma should go. This is used depending on the setting * USE_LINKER_PRAGMA, defined in trcConfig.h. * * If using GCC, this is instead done by adding a "section" attribute: * * RecorderDataType RecorderData __attribute__ ((section ("name"))) = ... * * Remember to replace "name" with the correct section name. ******************************************************************************/ static void vInitStartMarkers(void); #if (TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_STATIC) #if (USE_LINKER_PRAGMA == 1) #include "recorderdata_linker_pragma.h" #endif RecorderDataType RecorderData; #endif RecorderDataType* RecorderDataPtr = NULL; /* This version of the function dynamically allocates the trace data */ void prvTraceInitTraceData() { init_hwtc_count = HWTC_COUNT; #if TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_STATIC RecorderDataPtr = &RecorderData; #elif TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_DYNAMIC RecorderDataPtr = (RecorderDataType*)TRACE_MALLOC(sizeof(RecorderDataType)); #elif TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_CUSTOM /* DO NOTHING */ #endif TRACE_ASSERT(RecorderDataPtr != NULL, "prvTraceInitTraceData, RecorderDataPtr == NULL", ); if (! RecorderDataPtr) { vTraceError("No recorder data structure allocated!"); return; } (void)memset(RecorderDataPtr, 0, sizeof(RecorderDataType)); RecorderDataPtr->startmarker0 = 0x00; RecorderDataPtr->startmarker1 = 0x01; RecorderDataPtr->startmarker2 = 0x02; RecorderDataPtr->startmarker3 = 0x03; RecorderDataPtr->startmarker4 = 0x70; RecorderDataPtr->startmarker5 = 0x71; RecorderDataPtr->startmarker6 = 0x72; RecorderDataPtr->startmarker7 = 0x73; RecorderDataPtr->startmarker8 = 0xF0; RecorderDataPtr->startmarker9 = 0xF1; RecorderDataPtr->startmarker10 = 0xF2; RecorderDataPtr->startmarker11 = 0xF3; RecorderDataPtr->version = TRACE_KERNEL_VERSION; RecorderDataPtr->minor_version = TRACE_MINOR_VERSION; RecorderDataPtr->irq_priority_order = IRQ_PRIORITY_ORDER; RecorderDataPtr->filesize = sizeof(RecorderDataType); RecorderDataPtr->maxEvents = EVENT_BUFFER_SIZE; RecorderDataPtr->debugMarker0 = 0xF0F0F0F0; /* This function is kernel specific */ vTraceInitObjectPropertyTable(); RecorderDataPtr->debugMarker1 = 0xF1F1F1F1; RecorderDataPtr->SymbolTable.symTableSize = SYMBOL_TABLE_SIZE; RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1; #if (INCLUDE_FLOAT_SUPPORT == 1) RecorderDataPtr->exampleFloatEncoding = (float)1.0; /* otherwise already zero */ #endif RecorderDataPtr->debugMarker2 = 0xF2F2F2F2; (void)strncpy(RecorderDataPtr->systemInfo, TRACE_DESCRIPTION, TRACE_DESCRIPTION_MAX_LENGTH); RecorderDataPtr->debugMarker3 = 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 USE_SEPARATE_USER_EVENT_BUFFER RecorderDataPtr->userEventBuffer.bufferID = 1; RecorderDataPtr->userEventBuffer.version = 0; RecorderDataPtr->userEventBuffer.numberOfSlots = USER_EVENT_BUFFER_SIZE; RecorderDataPtr->userEventBuffer.numberOfChannels = CHANNEL_FORMAT_PAIRS + 1; #endif /* Kernel specific initialization of the objectHandleStacks variable */ vTraceInitObjectHandleStack(); /* Fix the start markers of the trace data structure */ vInitStartMarkers(); } static void vInitStartMarkers() { uint32_t i; uint8_t *ptr = (uint8_t*)&(RecorderDataPtr->startmarker0); if ((*ptr) == 0) { for (i = 0; i < 12; i++) { ptr[i] += 1; } } else { vTraceError("Trace start markers already initialized!"); } } volatile int recorder_busy = 0; /* Gives the last error message of the recorder. NULL if no error message. */ char* traceErrorMessage = NULL; void* xTraceNextFreeEventBufferSlot(void) { if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE) { vTraceError("Attempt to index outside event buffer!"); return NULL; } return (void*)(&RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex*4]); } uint16_t uiIndexOfObject(objectHandleType 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))); } vTraceError("Object table lookup with invalid object handle or object class!"); return 0; } /******************************************************************************* * Object handle system * This provides a mechanism to assign each kernel object (tasks, queues, etc) * with a 1-byte handle, that is used to identify the object in the trace. * This way, only one byte instead of four is necessary to identify the object. * This allows for maximum 255 objects, of each object class, active at any * moment. * Note that zero is reserved as an error code and is not a valid handle. * * In order to allow for fast dynamic allocation and release of object handles, * the handles of each object class (e.g., TASK) are stored in a stack. When a * handle is needed, e.g., on task creation, the next free handle is popped from * the stack. When an object (e.g., task) is deleted, its handle is pushed back * on the stack and can thereby be reused for other objects. * * Since this allows for reuse of object handles, a specific handle (e.g, "8") * may refer to TASK_X at one point, and later mean "TASK_Y". To resolve this, * the recorder uses "Close events", which are stored in the main event buffer * when objects are deleted and their handles are released. The close event * contains the mapping between object handle and object name which was valid up * to this point in time. The object name is stored as a symbol table entry. ******************************************************************************/ objectHandleType xTraceGetObjectHandle(traceObjectClass objectclass) { static objectHandleType handle; static int indexOfHandle; TRACE_ASSERT(objectclass < TRACE_NCLASSES, "xTraceGetObjectHandle: Invalid value for objectclass", (objectHandleType)0); 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] = (objectHandleType)(1 + indexOfHandle - objectHandleStacks.lowestIndexOfClass[objectclass]); } handle = objectHandleStacks.objectHandles[indexOfHandle]; if (objectHandleStacks.indexOfNextAvailableHandle[objectclass] > objectHandleStacks.highestIndexOfClass[objectclass]) { /* ERROR */ vTraceError(pszTraceGetErrorNotEnoughHandles(objectclass)); handle = 0; /* an invalid/anonymous handle - but the recorder is stopped now... */ } else { int hndCount; objectHandleStacks.indexOfNextAvailableHandle[objectclass]++; hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] - objectHandleStacks.lowestIndexOfClass[objectclass]; if (hndCount > objectHandleStacks.handleCountWaterMarksOfClass[objectclass]) { objectHandleStacks.handleCountWaterMarksOfClass[objectclass] = (objectHandleType)hndCount; } TRACE_CLEAR_OBJECT_FLAG_ISEXCLUDED(objectclass, handle); } return handle; } void vTraceFreeObjectHandle(traceObjectClass objectclass, objectHandleType handle) { int indexOfHandle; TRACE_ASSERT(objectclass < TRACE_NCLASSES, "vTraceFreeObjectHandle: Invalid value for objectclass", ); TRACE_ASSERT(handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], "vTraceFreeObjectHandle: Invalid value for handle", ); /* Check that there is room to push the handle on the stack */ if ((objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) < objectHandleStacks.lowestIndexOfClass[objectclass]) { /* Error */ vTraceError("Attempt to free more handles than allocated! (duplicate xTaskDelete or xQueueDelete?)"); } else { objectHandleStacks.indexOfNextAvailableHandle[objectclass]--; indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass]; objectHandleStacks.objectHandles[indexOfHandle] = handle; } } /******************************************************************************* * Objects Property Table * * This holds the names and properties of the currently active objects, such as * tasks and queues. This is developed to support "dynamic" objects which might * be deleted during runtime. Their handles are only valid during their * lifetime, i.e., from create to delete, as they might be reused on later * create operations. When an object is deleted from the OPT, its data is moved * to the trace buffer and/or the symbol table. * When an object (task, queue, etc.) is created, it receives a handle, which * together with the object class specifies its location in the OPT. Thus, * objects of different types may share the same name and/or handle, but still * be independent objects. ******************************************************************************/ /******************************************************************************* * vTraceSetObjectName * * 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 vTraceSetObjectName(traceObjectClass objectclass, objectHandleType handle, const char* name) { static uint16_t idx; TRACE_ASSERT(name != NULL, "vTraceSetObjectName: name == NULL", ); if (objectclass >= TRACE_NCLASSES) { vTraceError("Illegal object class in vTraceSetObjectName"); return; } if (handle == 0) { vTraceError("Illegal handle (0) in vTraceSetObjectName."); return; } if (handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]) { /* ERROR */ vTraceError(pszTraceGetErrorNotEnoughHandles(objectclass)); } else { idx = uiIndexOfObject(handle, objectclass); if (traceErrorMessage == NULL) { (void)strncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]), name, RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ]); } } } traceLabel prvTraceOpenSymbol(const char* name, traceLabel userEventChannel) { uint16_t result; uint8_t len; uint8_t crc; len = 0; crc = 0; TRACE_ASSERT(name != NULL, "prvTraceOpenSymbol: name == NULL", (traceLabel)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; } /******************************************************************************* * Supporting functions ******************************************************************************/ extern volatile uint32_t rtest_error_flag; /******************************************************************************* * vTraceError * * 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., NTask) in trcConfig.h is too small. ******************************************************************************/ void vTraceError(const char* msg) { TRACE_ASSERT(msg != NULL, "vTraceError: msg == NULL", ); TRACE_ASSERT(RecorderDataPtr != NULL, "vTraceError: RecorderDataPtr == NULL", ); // Stop the recorder. Note: We do not call vTraceStop, since that adds a weird // and unnecessary dependency to trcUser.c. RecorderDataPtr->recorderActive = 0; if (traceErrorMessage == NULL) { traceErrorMessage = (char*)msg; (void)strncpy(RecorderDataPtr->systemInfo, traceErrorMessage, TRACE_DESCRIPTION_MAX_LENGTH); RecorderDataPtr->internalErrorOccured = 1; } } /****************************************************************************** * 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 ringbuffer mode) * any data entries following must be replaced with NULL events (code 0). * * This is assumed to execute within a critical section... *****************************************************************************/ 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", ); 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, 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++; } } /******************************************************************************* * prvTraceUpdateCounters * * Updates the index of the event buffer. ******************************************************************************/ void prvTraceUpdateCounters(void) { if (RecorderDataPtr->recorderActive == 0) { return; } RecorderDataPtr->numEvents++; RecorderDataPtr->nextFreeIndex++; if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE) { #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER) RecorderDataPtr->bufferIsFull = 1; RecorderDataPtr->nextFreeIndex = 0; #else vTraceStop(); #endif } #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER) prvCheckDataToBeOverwrittenForMultiEntryEvents(1); #endif #ifdef STOP_AFTER_N_EVENTS #if (STOP_AFTER_N_EVENTS > -1) if (RecorderDataPtr->numEvents >= STOP_AFTER_N_EVENTS) { vTraceStop(); } #endif #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 && init_hwtc_count != HWTC_COUNT) { /* If HWTC_PERIOD is mapped to the timer reload register, such as in the Cortex M port, it might not be initialized before the Kernel scheduler has been started has been started. We therefore store the frequency of the timer once the counter register has changed. */ #if (SELECTED_PORT == PORT_Win32) RecorderDataPtr->frequency = 100000; #elif (SELECTED_PORT == PORT_HWIndependent) RecorderDataPtr->frequency = TRACE_TICK_RATE_HZ; #else RecorderDataPtr->frequency = (HWTC_PERIOD * TRACE_TICK_RATE_HZ) / (uint32_t)HWTC_DIVISOR; #endif } /************************************************************************** * 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. **************************************************************************/ vTracePortGetTimeStamp(×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*) xTraceNextFreeEventBufferSlot(); 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 { vTraceError("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 ******************************************************************************/ traceLabel prvTraceLookupSymbolTableEntry(const char* name, uint8_t crc6, uint8_t len, traceLabel chn) { uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ]; TRACE_ASSERT(name != NULL, "prvTraceLookupSymbolTableEntry: name == NULL", (traceLabel)0); TRACE_ASSERT(len != 0, "prvTraceLookupSymbolTableEntry: len == 0", (traceLabel)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, traceLabel 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 >= SYMBOL_TABLE_SIZE) { vTraceError("Symbol table full. Increase 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) */ (void)strncpy((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 += (len + 5); ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - (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 = 0; int crc = 0; TRACE_ASSERT(pname != NULL, "prvTraceGetChecksum: pname == NULL", ); TRACE_ASSERT(pcrc != NULL, "prvTraceGetChecksum: pcrc == NULL", ); TRACE_ASSERT(plength != NULL, "prvTraceGetChecksum: plength == NULL", ); if (pname != (const char *) 0) { for (; (c = *pname++) != '\0';) { crc += c; length++; } } *pcrc = (uint8_t)(crc & 0x3F); *plength = (uint8_t)length; } #endif