You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
993 lines
36 KiB
C
993 lines
36 KiB
C
/*******************************************************************************
|
|
* FreeRTOS+Trace v2.3.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 trcPort.c and trcPort.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.
|
|
*
|
|
* FreeRTOS+Trace is available as Free Edition and in two premium editions.
|
|
* You may use the premium features during 30 days for evaluation.
|
|
* Download FreeRTOS+Trace at http://www.percepio.com/products/downloads/
|
|
*
|
|
* Copyright Percepio AB, 2012.
|
|
* www.percepio.com
|
|
******************************************************************************/
|
|
|
|
#include "trcUser.h"
|
|
#include "task.h"
|
|
|
|
#if (configUSE_TRACE_FACILITY == 1)
|
|
|
|
/*******************************************************************************
|
|
* Static data initializations
|
|
******************************************************************************/
|
|
|
|
|
|
/*******************************************************************************
|
|
* RecorderData
|
|
*
|
|
* The main data structure. This is the data read by FreeRTOS+Trace, 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.
|
|
******************************************************************************/
|
|
|
|
#if (TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_STATIC)
|
|
#if (USE_LINKER_PRAGMA == 1)
|
|
#include "recorderdata_linker_pragma.h"
|
|
#endif
|
|
RecorderDataType RecorderData =
|
|
{
|
|
/* start marker, 12 chars */
|
|
0x01, 0x02, 0x03, 0x04,
|
|
0x71, 0x72, 0x73, 0x74,
|
|
0xF1, 0xF2, 0xF3, 0xF4,
|
|
|
|
/* version code - also used to determine endianness */
|
|
VERSION,
|
|
|
|
/* minor file format version */
|
|
MINOR_VERSION,
|
|
|
|
/* irq priority order */
|
|
IRQ_PRIORITY_ORDER,
|
|
|
|
/* file size (for control) */
|
|
sizeof(RecorderDataType),
|
|
|
|
/* number of events stored so far */
|
|
0,
|
|
|
|
/* size of events buffer (in event records, each 4 bytes) */
|
|
EVENT_BUFFER_SIZE,
|
|
|
|
/* next free event index (event index, not byte address) */
|
|
0,
|
|
|
|
/* buffer is full */
|
|
0,
|
|
|
|
/* frequency of clock user for timestamps, in Hz - should be 0 here
|
|
as this is used to indicate "not yet initialized" - this is instead
|
|
initialized on the first taskswitch event. */
|
|
0,
|
|
|
|
/* the absolute timestamp of the last stored event, modulo frequency */
|
|
0,
|
|
|
|
/* the number of seconds so far */
|
|
0,
|
|
|
|
/* is recorder active (yes = 1) - note that "close" events are always
|
|
stored to keep the name-handle mapping updated!*/
|
|
0,
|
|
|
|
/* Generated by FreeRTOS+Trace in Team Admin mode. Otherwise this should be "". */
|
|
TEAM_LICENSE_CODE,
|
|
|
|
/* debug marker 0 */
|
|
0xF0F0F0F0,
|
|
|
|
/* The Object Property Table - holds info of all active objects */
|
|
{
|
|
/* Number of object classes, also those not used */
|
|
NCLASSES,
|
|
|
|
/* The size in bytes of the object table byte pool */
|
|
DynObjTableSize,
|
|
|
|
/* The number of slots/handles available for each class */
|
|
{
|
|
NQueue,
|
|
NSemaphore,
|
|
NMutex,
|
|
NTask,
|
|
NISR
|
|
},
|
|
|
|
/* The maximum name length for each object class */
|
|
{
|
|
NameLenQueue,
|
|
NameLenSemaphore,
|
|
NameLenMutex,
|
|
NameLenTask,
|
|
NameLenISR
|
|
},
|
|
|
|
/* The total length a property table entry of the class */
|
|
{
|
|
PropertyTableSizeQueue,
|
|
PropertyTableSizeSemaphore,
|
|
PropertyTableSizeMutex,
|
|
PropertyTableSizeTask,
|
|
PropertyTableSizeISR
|
|
},
|
|
|
|
/* The start index of each class in the object property table */
|
|
{
|
|
StartIndexQueue,
|
|
StartIndexSemaphore,
|
|
StartIndexMutex,
|
|
StartIndexTask,
|
|
StartIndexISR
|
|
},
|
|
|
|
/* the object property table - encoded in a byte array using above
|
|
definitions */
|
|
{0}
|
|
},
|
|
|
|
/* debug marker 1 */
|
|
0xF1F1F1F1,
|
|
|
|
/* The Symbol Table - holds all object names used since system
|
|
startup. Every string is unique, so objects with same name will share
|
|
an entry. Each name entry has four extra bytes: byte 0-1 is a link
|
|
reference in an internal linked list, used for fast lookups, byte 2-3
|
|
holds a reference to a channel label used for vTracePrintF format
|
|
strings, and byte 4.. holds the object name, followed by a
|
|
zero-termination.*/
|
|
{
|
|
SYMBOL_TABLE_SIZE,
|
|
|
|
/* next free index (0 is reserved to mean NULL) */
|
|
1,
|
|
|
|
/* the symbol table byte pool */
|
|
{0},
|
|
|
|
/* this is a 64 entry array holding 16-bit references (indexes)
|
|
to the most recent entry of each checksum - i.e., list heads.*/
|
|
{0},
|
|
|
|
},
|
|
|
|
#if (INCLUDE_FLOAT_SUPPORT == 1)
|
|
/* example float, for float endian detection */
|
|
(float)1.0,
|
|
#else
|
|
/* This code signals that no float support is included */
|
|
(uint32_t)0,
|
|
#endif
|
|
|
|
/* internalErrorOccured */
|
|
0,
|
|
|
|
/* debug marker 2 */
|
|
0xF2F2F2F2,
|
|
|
|
/* The trace description string, can hold any information about the system,
|
|
e.g., version, configuration. Error messages from the recorder are
|
|
copied to this buffer. Also used for internal error messages.*/
|
|
TRACE_DESCRIPTION,
|
|
|
|
/* debug marker 3 */
|
|
0xF3F3F3F3,
|
|
|
|
/* the event data buffer, size EVENT_BUFFER_SIZE*4 */
|
|
{0},
|
|
|
|
/* end markers, used to extract the trace from a RAM dump image */
|
|
0x0A, 0x0B, 0x0C, 0x0D,
|
|
0x71, 0x72, 0x73, 0x74,
|
|
0xF1, 0xF2, 0xF3, 0xF4
|
|
};
|
|
|
|
RecorderDataType* RecorderDataPtr = &RecorderData;
|
|
|
|
/* This version of the function does nothing as the trace data is statically allocated */
|
|
RecorderDataType* xTraceInitTraceData(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#elif (TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_DYNAMIC)
|
|
|
|
RecorderDataType* RecorderDataPtr = NULL;
|
|
|
|
/* This version of the function dynamically allocates the trace data */
|
|
RecorderDataType* xTraceInitTraceData(void)
|
|
{
|
|
RecorderDataType* tmp = (RecorderDataType*)pvPortMalloc(sizeof(RecorderDataType));
|
|
|
|
if (! tmp)
|
|
{
|
|
vTraceError("Malloc failed in xTraceInitTraceData! Reduce size constants in trcConfig.h");
|
|
return NULL;
|
|
}
|
|
|
|
(void)memset(tmp, 0, sizeof(RecorderDataType));
|
|
|
|
tmp->startmarker0 = 0x01;
|
|
tmp->startmarker1 = 0x02;
|
|
tmp->startmarker2 = 0x03;
|
|
tmp->startmarker3 = 0x04;
|
|
tmp->startmarker4 = 0x71;
|
|
tmp->startmarker5 = 0x72;
|
|
tmp->startmarker6 = 0x73;
|
|
tmp->startmarker7 = 0x74;
|
|
tmp->startmarker8 = 0xF1;
|
|
tmp->startmarker9 = 0xF2;
|
|
tmp->startmarker10 = 0xF3;
|
|
tmp->startmarker11 = 0xF4;
|
|
tmp->version = VERSION;
|
|
tmp->minor_version = MINOR_VERSION;
|
|
tmp->irq_priority_order = IRQ_PRIORITY_ORDER;
|
|
tmp->filesize = sizeof(RecorderDataType);
|
|
|
|
tmp->maxEvents = EVENT_BUFFER_SIZE;
|
|
|
|
tmp->debugMarker0 = 0xF0F0F0F0;
|
|
tmp->ObjectPropertyTable.NumberOfObjectClasses = NCLASSES;
|
|
tmp->ObjectPropertyTable.ObjectPropertyTableSizeInBytes = DynObjTableSize;
|
|
tmp->ObjectPropertyTable.NumberOfObjectsPerClass[0] = NQueue;
|
|
tmp->ObjectPropertyTable.NumberOfObjectsPerClass[1] = NSemaphore;
|
|
tmp->ObjectPropertyTable.NumberOfObjectsPerClass[2] = NMutex;
|
|
tmp->ObjectPropertyTable.NumberOfObjectsPerClass[3] = NTask;
|
|
tmp->ObjectPropertyTable.NumberOfObjectsPerClass[4] = NISR;
|
|
tmp->ObjectPropertyTable.NameLengthPerClass[0] = NameLenQueue;
|
|
tmp->ObjectPropertyTable.NameLengthPerClass[1] = NameLenSemaphore;
|
|
tmp->ObjectPropertyTable.NameLengthPerClass[2] = NameLenMutex;
|
|
tmp->ObjectPropertyTable.NameLengthPerClass[3] = NameLenTask;
|
|
tmp->ObjectPropertyTable.NameLengthPerClass[4] = NameLenISR;
|
|
tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[0] = PropertyTableSizeQueue;
|
|
tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[1] = PropertyTableSizeSemaphore;
|
|
tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[2] = PropertyTableSizeMutex;
|
|
tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[3] = PropertyTableSizeTask;
|
|
tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[4] = PropertyTableSizeISR;
|
|
tmp->ObjectPropertyTable.StartIndexOfClass[0] = StartIndexQueue;
|
|
tmp->ObjectPropertyTable.StartIndexOfClass[1] = StartIndexSemaphore;
|
|
tmp->ObjectPropertyTable.StartIndexOfClass[2] = StartIndexMutex;
|
|
tmp->ObjectPropertyTable.StartIndexOfClass[3] = StartIndexTask;
|
|
tmp->ObjectPropertyTable.StartIndexOfClass[4] = StartIndexISR;
|
|
tmp->debugMarker1 = 0xF1F1F1F1;
|
|
tmp->SymbolTable.symTableSize = SYMBOL_TABLE_SIZE;
|
|
tmp->SymbolTable.nextFreeSymbolIndex = 1;
|
|
#if (INCLUDE_FLOAT_SUPPORT == 1)
|
|
tmp->exampleFloatEncoding = (float)1.0; /* otherwize already zero */
|
|
#endif
|
|
tmp->debugMarker2 = 0xF2F2F2F2;
|
|
(void)strncpy(tmp->systemInfo, TRACE_DESCRIPTION, TRACE_DESCRIPTION_MAX_LENGTH);
|
|
tmp->debugMarker3 = 0xF3F3F3F3;
|
|
tmp->endmarker0 = 0x0A;
|
|
tmp->endmarker1 = 0x0B;
|
|
tmp->endmarker2 = 0x0C;
|
|
tmp->endmarker3 = 0x0D;
|
|
tmp->endmarker4 = 0x71;
|
|
tmp->endmarker5 = 0x72;
|
|
tmp->endmarker6 = 0x73;
|
|
tmp->endmarker7 = 0x74;
|
|
tmp->endmarker8 = 0xF1;
|
|
tmp->endmarker9 = 0xF2;
|
|
tmp->endmarker10 = 0xF3;
|
|
tmp->endmarker11 = 0xF4;
|
|
|
|
RecorderDataPtr = tmp;
|
|
|
|
return (RecorderDataType*)RecorderDataPtr;
|
|
}
|
|
|
|
#endif
|
|
|
|
volatile int recorder_busy = 0;
|
|
|
|
char sprintfBuffer[150];
|
|
|
|
/* For debug printouts - the names of the object classes */
|
|
char OBJECTCLASSNAME[NCLASSES][10] =
|
|
{
|
|
"QUEUE",
|
|
"SEMAPHORE",
|
|
"MUTEX",
|
|
"TASK",
|
|
"ISR"
|
|
};
|
|
|
|
/* Initialization of the handle mechanism, see e.g, xTraceGetObjectHandle */
|
|
objectHandleStackType objectHandleStacks =
|
|
{
|
|
/* indexOfNextAvailableHandle */
|
|
{
|
|
0,
|
|
NQueue,
|
|
NQueue + NSemaphore,
|
|
NQueue + NSemaphore + NMutex,
|
|
NQueue + NSemaphore + NMutex + NTask
|
|
},
|
|
|
|
/* lowestIndexOfClass */
|
|
{
|
|
0,
|
|
NQueue,
|
|
NQueue + NSemaphore,
|
|
NQueue + NSemaphore + NMutex,
|
|
NQueue + NSemaphore + NMutex + NTask
|
|
},
|
|
|
|
/* highestIndexOfClass */
|
|
{
|
|
NQueue - 1,
|
|
NQueue + NSemaphore - 1,
|
|
NQueue + NSemaphore + NMutex - 1,
|
|
NQueue + NSemaphore + NMutex + NTask - 1,
|
|
NQueue + NSemaphore + NMutex + NTask + NISR - 1
|
|
},
|
|
{0},
|
|
{0}
|
|
};
|
|
|
|
|
|
/* Used for internal state flags of objects */
|
|
uint8_t excludedFlags[(NEventCodes+NQueue+NSemaphore+NMutex+NTask) / 8 + 1];
|
|
uint8_t ifeFlags[NTask / 8 + 1];
|
|
|
|
/* 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)
|
|
{
|
|
if ((objectclass < 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;
|
|
|
|
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 */
|
|
switch(objectclass)
|
|
{
|
|
case TRACE_CLASS_TASK:
|
|
vTraceError("Not enough TASK handles - increase NTask in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_ISR:
|
|
vTraceError("Not enough ISR handles - increase NISR in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_SEMAPHORE:
|
|
vTraceError("Not enough SEMAPHORE handles - increase NSemaphore in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_MUTEX:
|
|
vTraceError("Not enough MUTEX handles - increase NMutex in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_QUEUE:
|
|
vTraceError("Not enough QUEUE handles - increase NQueue in trcConfig.h");
|
|
break;
|
|
default:
|
|
vTraceError("Invalid object class.");
|
|
break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
void vTraceFreeObjectHandle(traceObjectClass objectclass, objectHandleType handle)
|
|
{
|
|
int indexOfHandle;
|
|
|
|
/* 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;
|
|
|
|
if (handle == 0)
|
|
{
|
|
vTraceError("Illegal handle (0) in vTraceSetObjectName.");
|
|
return;
|
|
}
|
|
|
|
switch(objectclass)
|
|
{
|
|
case TRACE_CLASS_TASK:
|
|
case TRACE_CLASS_ISR:
|
|
case TRACE_CLASS_SEMAPHORE:
|
|
case TRACE_CLASS_MUTEX:
|
|
case TRACE_CLASS_QUEUE:
|
|
break;
|
|
default:
|
|
vTraceError("Illegal object class in vTraceSetObjectName");
|
|
break;
|
|
}
|
|
|
|
if (handle >
|
|
RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass])
|
|
{
|
|
switch(objectclass)
|
|
{
|
|
case TRACE_CLASS_TASK:
|
|
vTraceError("Not enough TASK handles - increase NTask in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_ISR:
|
|
vTraceError("Not enough ISR handles - increase NISR in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_SEMAPHORE:
|
|
vTraceError("Not enough SEMAPHORE handles - increase NSemaphore in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_MUTEX:
|
|
vTraceError("Not enough MUTEX handles - increase NMutex in trcConfig.h");
|
|
break;
|
|
case TRACE_CLASS_QUEUE:
|
|
vTraceError("Not enough QUEUE handles - increase NQueue in trcConfig.h");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
idx = uiIndexOfObject(handle, objectclass);
|
|
|
|
if (traceErrorMessage == NULL)
|
|
{
|
|
(void)strncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]),
|
|
name,
|
|
RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ] );
|
|
#ifdef WIN32
|
|
printf("vTraceSetObjectName(%d, %d, %s)\n", objectclass, handle, name);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
traceLabel prvTraceOpenSymbol(const char* name, traceLabel userEventChannel)
|
|
{
|
|
static uint16_t result;
|
|
static uint8_t len;
|
|
static uint8_t crc;
|
|
len = 0;
|
|
crc = 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
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* 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)
|
|
{
|
|
vTraceStop();
|
|
if (traceErrorMessage == NULL)
|
|
{
|
|
traceErrorMessage = (char*)msg;
|
|
(void)strncpy(RecorderDataPtr->systemInfo,
|
|
traceErrorMessage,
|
|
TRACE_DESCRIPTION_MAX_LENGTH);
|
|
RecorderDataPtr->internalErrorOccured = 1;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* prvCheckDataToBeOverwrittenForMultiEntryUserEvents
|
|
*
|
|
* 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 prvCheckDataToBeOverwrittenForMultiEntryUserEvents(
|
|
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;
|
|
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);
|
|
}
|
|
}
|
|
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 (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)
|
|
RecorderDataPtr->bufferIsFull = 1;
|
|
RecorderDataPtr->nextFreeIndex = 0;
|
|
#else
|
|
vTraceStop();
|
|
#endif
|
|
}
|
|
|
|
#if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)
|
|
prvCheckDataToBeOverwrittenForMultiEntryUserEvents(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;
|
|
|
|
if (RecorderDataPtr->frequency == 0)
|
|
{
|
|
/* If HWTC_PERIOD is mapped to the timer reload register,
|
|
such as in the Cortex M port, it is not initialized before
|
|
FreeRTOS has been started. We therefore store the frequency
|
|
of the timer at the first timestamped event after the
|
|
scheduler has started. (Note that this function is called
|
|
also by vTraceStart and uiTraceStart, which might be
|
|
called before the scheduler has been started.) */
|
|
|
|
#if (SELECTED_PORT == PORT_Win32)
|
|
RecorderDataPtr->frequency = 100000;
|
|
#elif (SELECTED_PORT == PORT_HWIndependent)
|
|
RecorderDataPtr->frequency = configTICK_RATE_HZ;
|
|
#else
|
|
if (xTaskGetSchedulerState() != 0) /* Has the scheduler started? */
|
|
{
|
|
RecorderDataPtr->frequency =
|
|
(uint32_t)HWTC_PERIOD * (uint32_t)configTICK_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.
|
|
**************************************************************************/
|
|
|
|
uiTracePortGetTimeStamp(×tamp);
|
|
|
|
/***************************************************************************
|
|
* This condition is only for the Win32 port, since it does not use the tick
|
|
* count but instead only HWTC_COUNT (from the performance counter).
|
|
* Without this condition, you sometimes get a negative dts value (converted
|
|
* into a very large unsiged value) when the performance counter wraps
|
|
* around. In other "normal" ports also using the FreeRTOS tick counter, this
|
|
* condition can not occur and therefore has no impact.
|
|
***************************************************************************/
|
|
if (timestamp < old_timestamp)
|
|
{
|
|
timestamp += RecorderDataPtr->frequency;
|
|
}
|
|
|
|
|
|
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 + 1));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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 ];
|
|
|
|
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;
|
|
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 offest 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;
|
|
if ( pname != (const char *) 0 )
|
|
{
|
|
for ( ; (c = *pname++) != '\0'; )
|
|
{
|
|
crc += c;
|
|
length++;
|
|
}
|
|
}
|
|
*pcrc = (uint8_t)(crc % 64);
|
|
*plength = (uint8_t)length;
|
|
}
|
|
|
|
#endif
|