blob: bfd6cb28c3eadb77083e5a89eaf30f8b553a8d5e [file] [log] [blame]
/*******************************************************************************
* 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 <stdint.h>
/*******************************************************************************
* 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(&timestamp);
/***************************************************************************
* 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