mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-08-18 09:08:33 -04:00
Add FreeRTOS-Plus directory with new directory structure so it matches the FreeRTOS directory.
This commit is contained in:
parent
80f7e8cdd4
commit
64a3ab321a
528 changed files with 228252 additions and 0 deletions
992
FreeRTOS-Plus/Source/FreeRTOS-Plus-Trace/trcBase.c
Normal file
992
FreeRTOS-Plus/Source/FreeRTOS-Plus-Trace/trcBase.c
Normal file
|
@ -0,0 +1,992 @@
|
|||
/*******************************************************************************
|
||||
* 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
|
Loading…
Add table
Add a link
Reference in a new issue