/******************************************************************************* * Trace Recorder Library for Tracealyzer v3.0.2 * Percepio AB, www.percepio.com * * trcRecorder.c * * The trace recorder core functions (portable). * * Terms of Use * This software (the "Tracealyzer Recorder Library") 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. * * Separate conditions applies for the SEGGER branded source code included. * * The recorder library is free for use together with Percepio products. * You may distribute the recorder library in its original form, but public * distribution of modified versions require approval 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. * * Tabs are used for indent in this file (1 tab = 4 spaces) * * Copyright Percepio AB, 2015. * www.percepio.com ******************************************************************************/ #include #include #include "trcRecorder.h" #include "trcStreamPort.h" #if (USE_TRACEALYZER_RECORDER == 1) uint32_t uiTraceTickCount = 0; typedef struct{ int16_t EventID; uint16_t EventCount; uint32_t TS; } BaseEvent; typedef struct{ BaseEvent base; uint32_t param1; } EventWithParam_1; typedef struct{ BaseEvent base; uint32_t param1; uint32_t param2; } EventWithParam_2; typedef struct{ BaseEvent base; uint32_t param1; uint32_t param2; uint32_t param3; } EventWithParam_3; /* Used in event functions with variable number of parameters. */ typedef struct { BaseEvent base; char data[60]; /* maximum payload size */ } largestEventType; typedef struct{ uint32_t psf; uint16_t version; uint16_t platform; uint32_t options; uint16_t symbolSize; uint16_t symbolCount; uint16_t objectDataSize; uint16_t objectDataCount; } PSFHeaderInfo; /* The size of each slot in the Symbol Table */ #define SYMBOL_TABLE_SLOT_SIZE (sizeof(uint32_t) + (((TRC_SYMBOL_MAX_LENGTH)+(sizeof(uint32_t)-1))/sizeof(uint32_t))*sizeof(uint32_t)) #define OBJECT_DATA_SLOT_SIZE (sizeof(uint32_t) + sizeof(uint32_t)) /* The total size of the Symbol Table */ #define SYMBOL_TABLE_BUFFER_SIZE (TRC_SYMBOL_TABLE_SLOTS * SYMBOL_TABLE_SLOT_SIZE) /* The total size of the Object Data Table */ #define OBJECT_DATA_TABLE_BUFFER_SIZE (TRC_OBJECT_DATA_SLOTS * OBJECT_DATA_SLOT_SIZE) /* The Symbol Table type - just a byte array */ typedef struct{ uint8_t pSymbolTableBuffer[SYMBOL_TABLE_BUFFER_SIZE]; } SymbolTable; /* The Object Data Table type - just a byte array */ typedef struct{ uint8_t pObjectDataTableBuffer[OBJECT_DATA_TABLE_BUFFER_SIZE]; } ObjectDataTable; /* The Symbol Table instance - keeps names of tasks and other named objects. */ static SymbolTable symbolTable = { { 0 } }; /* The Object Data Table instance - keeps initial priorities of tasks. */ static ObjectDataTable objectDataTable = { { 0 } }; /* Code used for "task address" when no task has started. (NULL = idle task) */ #define HANDLE_NO_TASK 2 /* The maximum number of nested ISRs */ #define MAX_ISR_NESTING 8 /* Keeps track of ISR nesting */ static uint32_t ISR_stack[MAX_ISR_NESTING]; /* Keeps track of ISR nesting */ static int8_t ISR_stack_index = -1; /* Any error that occured in the recorder (also creates User Event) */ static int errorCode = 0; /* The user event channel for recorder warnings, defined in trcKernelPort.c */ extern char* trcWarningChannel; /* Performs timestamping using definitions in trcHardwarePort.h */ static uint32_t prvGetTimestamp32(void); /* Counts the number of trace sessions (not yet used) */ static uint32_t SessionCounter = 0; /* Master switch for recording (0 => Disabled, 1 => Enabled) */ static uint32_t RecorderEnabled = 0; /* Used to determine endian of data (big/little) */ static uint32_t PSFEndianessIdentifier = 0x50534600; /* Used to interpret the data format */ static uint16_t FormatVersion = 0x0002; /* The number of events stored. Used as event sequence number. */ static uint32_t eventCounter = 0; /* Keeps track of if the current ISR chain has triggered a context switch that will be performed once all ISRs have returned. */ int32_t isPendingContextSwitch = 0; /******************************************************************************* * NoRoomForSymbol * * Incremented on vTraceSaveSymbol if no room for saving the symbol name. This * is used for storing the names of: * - Tasks * - Named ISRs (vTraceSetISRProperties) * - Named kernel objects (vTraceStoreKernelObjectName) * - User event channels (vTraceStoreUserEventChannelName) * * This variable should be zero. If not, it shows the number of missing slots so * far. In that case, increment SYMBOL_TABLE_SLOTS with (at least) this value. ******************************************************************************/ volatile uint32_t NoRoomForSymbol = 0; /******************************************************************************* * NoRoomForObjectData * * Incremented on vTraceSaveObjectData if no room for saving the object data, * i.e., the base priorities of tasks. There must be one slot for each task. * If not, this variable will show the difference. * * This variable should be zero. If not, it shows the number of missing slots so * far. In that case, increment OBJECT_DATA_SLOTS with (at least) this value. ******************************************************************************/ volatile uint32_t NoRoomForObjectData = 0; /******************************************************************************* * LongestSymbolName * * Updated in vTraceSaveSymbol. Should not exceed SYMBOL_MAX_LENGTH, otherwise * symbol names will be truncated. In that case, set SYMBOL_MAX_LENGTH to (at * least) this value. ******************************************************************************/ volatile uint32_t LongestSymbolName = 0; /******************************************************************************* * MaxBytesTruncated * * Set in prvTraceStoreStringEvent if the total data payload exceeds 60 bytes, * including data arguments and the string. For user events, that is 52 bytes * for string and data arguments. In that is exceeded, the event is truncated * (usually only the string, unless more than 15 parameters) and this variable * holds the maximum number of truncated bytes, from any event. ******************************************************************************/ volatile uint32_t MaxBytesTruncated = 0; /* Internal common function for storing string events */ static void prvTraceStoreStringEvent( int nArgs, uint16_t eventID, const char* userEvtChannel, const char* str, va_list vl); /* Stores the header information on Start */ static void vTraceStoreHeader(void); /* Stores the symbol table on Start */ static void vTraceStoreSymbolTable(void); /* Stores the object table on Start */ static void vTraceStoreObjectDataTable(void); /* Store the Timestamp Config on Start */ static void vTraceStoreTSConfig(void); /* Internal function for starting/stopping the recorder. */ static void intSetRecorderEnabled(int isEnabled); /* Command codes for TzCtrl task (sent on Start/Stop). */ #define CMD_SET_ACTIVE 1 /* The final command code, used to validate commands. Only one command yet. */ #define CMD_LAST_COMMAND 1 /* Part of the PSF format - encodes the number of 32-bit params in an event */ #define PARAM_COUNT(n) ((n & 0xF) << 12) /* Temporary fix since embOS sources aren't yet updated to contain them */ #ifndef OS_TRACE_ID_IFE #define OS_TRACE_ID_IFE (4000u) #endif #ifndef OS_TRACE_ID_IFE_NEXT #define OS_TRACE_ID_IFE_NEXT (4001u) #endif /****************************************************************************** * vTraceInstanceFinishNow * * Creates an event that ends the current task instance at this very instant. * This makes the viewer to splits the current fragment at this point and begin * a new actor instance, even if no task-switch has occurred. *****************************************************************************/ void vTraceInstanceFinishedNow(void) { vTraceStoreEvent0(PSF_EVENT_IFE_DIRECT); } /****************************************************************************** * vTraceInstanceFinishedNext * * Marks the current "task instance" as finished on the next kernel call. * * If that kernel call is blocking, the instance ends after the blocking event * and the corresponding return event is then the start of the next instance. * If the kernel call is not blocking, the viewer instead splits the current * fragment right before the kernel call, which makes this call the first event * of the next instance. *****************************************************************************/ void vTraceInstanceFinishedNext(void) { vTraceStoreEvent0(PSF_EVENT_IFE_NEXT); } /******************************************************************************* * vTraceStoreUserEventChannelName * * Stores a name for a user event channel, returns the handle (just a pointer * to the const char*. * The returned channel handle ******************************************************************************/ char* vTraceStoreUserEventChannelName(const char* name) { vTraceSaveSymbol((void*)name, name); /* Always save in symbol table, if the recording has not yet started */ vTraceStoreStringEvent(1, PSF_EVENT_OBJ_NAME, name, (uint32_t)name); return (char*)name; } /******************************************************************************* * vTraceStoreKernelObjectName * * Stores a name for kernel objects (Semaphore, Mailbox, etc.). ******************************************************************************/ void vTraceStoreKernelObjectName(void* object, const char* name) { vTraceSaveSymbol(object, name); /* Always save in symbol table, if the recording has not yet started */ vTraceStoreStringEvent(1, PSF_EVENT_OBJ_NAME, name, (uint32_t)object); } /****************************************************************************** * vTracePrint * * Generates "User Events", with unformatted text. * * User Events can be used for very efficient application logging, and are shown * as yellow labels in the main trace view. * * You may group User Events into User Event Channels. The yellow User Event * labels shows the logged string, preceeded by the channel name within * brackets. For example: * * "[MyChannel] Hello World!" * * The User Event Channels are shown in the View Filter, which makes it easy to * select what User Events you wish to display. User Event Channels are created * using vTraceStoreUserEventChannelName(). * * Example: * * char* error_uechannel = vTraceStoreUserEventChannelName("Errors"); * ... * vTracePrint(error_uechannel, "Shouldn't reach this code!"); * ******************************************************************************/ void vTracePrint(const char* chn, const char* str) { va_list vl = { 0 }; if (chn != NULL) { prvTraceStoreStringEvent(0, PSF_EVENT_USER_EVENT + 1, chn, str, vl); } else { prvTraceStoreStringEvent(0, PSF_EVENT_USER_EVENT, chn, str, vl); } } /****************************************************************************** * vTracePrintF * * Generates "User Events", with formatted text and data, similar to a "printf". * It is very fast since the actual formatting is done on the host side when the * trace is displayed. * * User Events can be used for very efficient application logging, and are shown * as yellow labels in the main trace view. * An advantage of User Events is that data can be plotted in the "User Event * Signal Plot" view, visualizing any data you log as User Events, discrete * states or control system signals (e.g. system inputs or outputs). * * You may group User Events into User Event Channels. The yellow User Event * labels show the logged string, preceeded by the channel name within brackets. * * Example: * * "[MyChannel] Hello World!" * * The User Event Channels are shown in the View Filter, which makes it easy to * select what User Events you wish to display. User Event Channels are created * using vTraceStoreUserEventChannelName(). * * Example: * * char* adc_uechannel = vTraceStoreUserEventChannelName("ADC User Events"); * ... * vTracePrint(adc_uechannel, * "ADC channel %d: %lf volts", * ch, (double)adc_reading/(double)scale); * * All data arguments are assumed to be 32 bt wide. The following formats are * supported in v2.8: * %d - signed integer. The following width and padding format is supported: "%05d" -> "-0042" and "%5d" -> " -42" * %u - unsigned integer. The following width and padding format is supported: "%05u" -> "00042" and "%5u" -> " 42" * %X - hexadecimal (uppercase). The following width and padding format is supported: "%04X" -> "002A" and "%4X" -> " 2A" * %x - hexadecimal (lowercase). The following width and padding format is supported: "%04x" -> "002a" and "%4x" -> " 2a" * %s - string (currently, this must be an earlier stored symbol name) * * Up to 15 data arguments are allowed, with a total size of maximum 60 byte * including 8 byte for the base event fields and the format string. So with * one data argument, the maximum string length is 48 chars. If this is exceeded * the string is truncated (4 bytes at a time). * ******************************************************************************/ void vTracePrintF(const char* chn, const char* fmt, ...) { int i = 0; va_list vl; int nArgs = 0; int len = 0; for (len = 0; fmt[len] != 0; len++) { // Empty } if (len > 52) len = 52; while (i < len) { if (fmt[i] == '%') { if (fmt[i+1] != '%') nArgs++; /* Found an argument */ i++; /* Move past format specifier or non-argument '%' */ } i++; } va_start(vl, fmt); if (chn != NULL) { prvTraceStoreStringEvent(nArgs, PSF_EVENT_USER_EVENT + nArgs + 1, chn, fmt, vl); } else { prvTraceStoreStringEvent(nArgs, PSF_EVENT_USER_EVENT + nArgs, chn, fmt, vl); } va_end(vl); } /******************************************************************************* * vTraceSetISRProperties * * Stores a name and priority level for an Interrupt Service Routine, to allow * for better visualization. The string address is used as a unique handle. * * Example: * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt * ... * vTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); * ... * void ISR_handler() * { * vTraceStoreISRBegin("ISRTimer1"); * ... * vTraceStoreISREnd(0); * } * ******************************************************************************/ void vTraceSetISRProperties(const char* name, char priority) { /* Save object data in object data table */ vTraceSaveObjectData((void*)name, priority); /* Note: "name" is used both as a string argument, and the address as ID */ vTraceStoreStringEvent(2, PSF_EVENT_DEFINE_ISR, name, name, priority); /* Always save in symbol table, if the recording has not yet started */ vTraceSaveSymbol((void*)name, name); } /******************************************************************************* * vTraceStoreISRBegin * * Registers the beginning of an Interrupt Service Routine. * * Example: * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt * ... * vTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); * ... * void ISR_handler() * { * vTraceStoreISRBegin("ISRTimer1"); * ... * vTraceStoreISREnd(0); * } * ******************************************************************************/ void vTraceStoreISRBegin(void* handle) { TRACE_ALLOC_CRITICAL_SECTION(); TRACE_ENTER_CRITICAL_SECTION(); if (ISR_stack_index == -1) isPendingContextSwitch = 0; /* We are at the start of a possible ISR chain. No context switches should have been triggered now. */ if (ISR_stack_index < MAX_ISR_NESTING - 1) { ISR_stack_index++; ISR_stack[ISR_stack_index] = (uint32_t)handle; vTraceStoreEvent1(PSF_EVENT_ISR_BEGIN, (uint32_t)handle); TRACE_EXIT_CRITICAL_SECTION(); } else { TRACE_EXIT_CRITICAL_SECTION(); psfError(PSF_ERROR_ISR_NESTING_OVERFLOW); } } /******************************************************************************* * vTraceStoreISREnd * * Registers the end of an Interrupt Service Routine. * * This function will automatically detect if a task switch will take place * when interrupt ends. If this is possible depends on the kernel port. * * Example: * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt * ... * vTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); * ... * void ISR_handler() * { * vTraceStoreISRBegin("ISRTimer1"); * ... * vTraceStoreISREnd(); * } * ******************************************************************************/ void vTraceStoreISREnd() { vTraceStoreISREndManual(OS_IS_SWITCH_FROM_INT_REQUIRED()); } /******************************************************************************* * vTraceStoreISREndManual * * Registers the end of an Interrupt Service Routine. * * The parameter taskSwitchRequested indicates if the interrupt has requested a * task-switch (= 1) or if the interrupt returns to the earlier context (= 0) * * Example: * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt * ... * vTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1); * ... * void ISR_handler() * { * vTraceStoreISRBegin("ISRTimer1"); * ... * vTraceStoreISREndManual(0); * } * ******************************************************************************/ void vTraceStoreISREndManual(int isTaskSwitchRequired) { TRACE_ALLOC_CRITICAL_SECTION(); TRACE_ENTER_CRITICAL_SECTION(); isPendingContextSwitch |= isTaskSwitchRequired; /* Is there a pending context switch right now? */ if (ISR_stack_index > 0) { ISR_stack_index--; /* Store return to interrupted ISR (if nested ISRs)*/ vTraceStoreEvent1(PSF_EVENT_ISR_RESUME, (uint32_t)ISR_stack[ISR_stack_index]); } else { ISR_stack_index--; /* Store return to interrupted task, if a task switch has not been triggered by any interrupt */ if (isPendingContextSwitch == 0) { vTraceStoreEvent1(PSF_EVENT_TS_RESUME, (uint32_t)TRACE_GET_CURRENT_TASK()); } } TRACE_EXIT_CRITICAL_SECTION(); } /******************************************************************************/ /*** INTERNAL FUNCTIONS *******************************************************/ /******************************************************************************/ /* Internal function for starting/stopping the recorder. */ static void intSetRecorderEnabled(int isEnabled) { TRACE_ALLOC_CRITICAL_SECTION(); void* currentTask = TRACE_GET_CURRENT_TASK(); TRACE_ENTER_CRITICAL_SECTION(); RecorderEnabled = isEnabled; if (currentTask == NULL) { currentTask = (void*)HANDLE_NO_TASK; } if (RecorderEnabled) { vTraceOnTraceBegin(); eventCounter = 0; ISR_stack_index = -1; vTraceStoreHeader(); vTraceStoreSymbolTable(); vTraceStoreObjectDataTable(); vTraceStoreEvent3( PSF_EVENT_TRACE_START, (uint32_t)TRACE_GET_OS_TICKS(), (uint32_t)currentTask, SessionCounter++); vTraceStoreTSConfig(); } else { vTraceOnTraceEnd(); } TRACE_EXIT_CRITICAL_SECTION(); } /* Stores the symbol table on Start */ static void vTraceStoreSymbolTable() { uint32_t i = 0; uint32_t j = 0; TRACE_ALLOC_CRITICAL_SECTION(); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { for (i = 0; i < sizeof(SymbolTable); i += SYMBOL_TABLE_SLOT_SIZE) { TRC_STREAM_PORT_ALLOCATE_EVENT(uint8_t, data, SYMBOL_TABLE_SLOT_SIZE); for (j = 0; j < SYMBOL_TABLE_SLOT_SIZE; j++) { data[j] = symbolTable.pSymbolTableBuffer[i+j]; } TRC_STREAM_PORT_COMMIT_EVENT(data, SYMBOL_TABLE_SLOT_SIZE); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Stores the object table on Start */ static void vTraceStoreObjectDataTable() { uint32_t i = 0; uint32_t j = 0; TRACE_ALLOC_CRITICAL_SECTION(); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { for (i = 0; i < sizeof(ObjectDataTable); i += OBJECT_DATA_SLOT_SIZE) { TRC_STREAM_PORT_ALLOCATE_EVENT(uint8_t, data, OBJECT_DATA_SLOT_SIZE); for (j = 0; j < OBJECT_DATA_SLOT_SIZE; j++) { data[j] = objectDataTable.pObjectDataTableBuffer[i+j]; } TRC_STREAM_PORT_COMMIT_EVENT(data, OBJECT_DATA_SLOT_SIZE); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Stores the header information on Start */ static void vTraceStoreHeader() { TRACE_ALLOC_CRITICAL_SECTION(); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { TRC_STREAM_PORT_ALLOCATE_EVENT(PSFHeaderInfo, header, sizeof(PSFHeaderInfo)); if (header != NULL) { header->psf = PSFEndianessIdentifier; header->version = FormatVersion; header->platform = KERNEL_ID; header->options = 0; /* Lowest bit used for IRQ_PRIORITY_ORDER */ header->options = header->options | (IRQ_PRIORITY_ORDER << 0); header->symbolSize = SYMBOL_TABLE_SLOT_SIZE; header->symbolCount = TRC_SYMBOL_TABLE_SLOTS; header->objectDataSize = 8; header->objectDataCount = TRC_OBJECT_DATA_SLOTS; TRC_STREAM_PORT_COMMIT_EVENT(header, sizeof(PSFHeaderInfo)); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Store an event with zero parameters (event ID only) */ void vTraceStoreEvent0(uint16_t eventID) { TRACE_ALLOC_CRITICAL_SECTION(); PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { eventCounter++; TRC_STREAM_PORT_ALLOCATE_EVENT(BaseEvent, event, sizeof(BaseEvent)); if (event != NULL) { event->EventID = eventID | PARAM_COUNT(0); event->EventCount = eventCounter; event->TS = prvGetTimestamp32(); TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(BaseEvent)); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Store an event with one 32-bit parameter (pointer address or an int) */ void vTraceStoreEvent1(uint16_t eventID, uint32_t param1) { TRACE_ALLOC_CRITICAL_SECTION(); PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { eventCounter++; TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_1, event, sizeof(EventWithParam_1)); if (event != NULL) { event->base.EventID = eventID | PARAM_COUNT(1); event->base.EventCount = eventCounter; event->base.TS = prvGetTimestamp32(); event->param1 = (uint32_t)param1; TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_1)); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Store an event with two 32-bit parameters */ void vTraceStoreEvent2(uint16_t eventID, uint32_t param1, uint32_t param2) { TRACE_ALLOC_CRITICAL_SECTION(); PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { eventCounter++; TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_2, event, sizeof(EventWithParam_2)); if (event != NULL) { event->base.EventID = eventID | PARAM_COUNT(2); event->base.EventCount = eventCounter; event->base.TS = prvGetTimestamp32(); event->param1 = (uint32_t)param1; event->param2 = param2; TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_2)); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Store an event with three 32-bit parameters */ void vTraceStoreEvent3( uint16_t eventID, uint32_t param1, uint32_t param2, uint32_t param3) { TRACE_ALLOC_CRITICAL_SECTION(); PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { eventCounter++; TRC_STREAM_PORT_ALLOCATE_EVENT(EventWithParam_3, event, sizeof(EventWithParam_3)); if (event != NULL) { event->base.EventID = eventID | PARAM_COUNT(3); event->base.EventCount = eventCounter; event->base.TS = prvGetTimestamp32(); event->param1 = (uint32_t)param1; event->param2 = param2; event->param3 = param3; TRC_STREAM_PORT_COMMIT_EVENT(event, sizeof(EventWithParam_3)); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Stores an event with 32-bit integer parameters */ void vTraceStoreEvent(int nParam, uint16_t eventID, ...) { TRACE_ALLOC_CRITICAL_SECTION(); va_list vl; int i; PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE); TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { int eventSize = sizeof(BaseEvent) + nParam * sizeof(uint32_t); eventCounter++; TRC_STREAM_PORT_ALLOCATE_EVENT(largestEventType, event, eventSize); if (event != NULL) { event->base.EventID = eventID | PARAM_COUNT(nParam); event->base.EventCount = eventCounter; event->base.TS = prvGetTimestamp32(); va_start(vl, eventID); for (i = 0; i < nParam; i++) { uint32_t* tmp = (uint32_t*) &(event->data[i * 4]); *tmp = va_arg(vl, uint32_t); } va_end(vl); TRC_STREAM_PORT_COMMIT_EVENT(event, eventSize); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Stories an event with a string and 32-bit integer parameters */ void vTraceStoreStringEvent(int nArgs, uint16_t eventID, const char* str, ...) { va_list vl; va_start(vl, str); prvTraceStoreStringEvent(nArgs, eventID, NULL, str, vl); va_end(vl); } /* Internal common function for storing string events */ static void prvTraceStoreStringEvent( int nArgs, uint16_t eventID, const char* userEvtChannel, const char* str, va_list vl) { TRACE_ALLOC_CRITICAL_SECTION(); int len; int nParam; int strParam; int i; PSF_ASSERT(eventID < 4096, PSF_ERROR_EVENT_CODE_TOO_LARGE); len = 0; for (len = 0; str[len] != 0; len++) { // Empty } /* The string length in multiples of 32 bit words (+1 for null character) */ strParam = (len+1+3)/4; /* If a user event channel is specified, add in the list */ if (userEvtChannel) nArgs++; /* The total number of 32-bit words needed for the whole payload */ nParam = strParam + nArgs; if (nParam > 15) /* if attempting to store more than 60 byte (= max) */ { /* Truncate event if too large. The string characters are stored last, so usually only the string is truncated, unless there a lot of parameters... */ /* Diagnostics ... */ uint32_t bytesTrucated = (nParam - 15) * 4; if (bytesTrucated > MaxBytesTruncated) { MaxBytesTruncated = bytesTrucated; } nParam = 15; } TRACE_ENTER_CRITICAL_SECTION(); if (RecorderEnabled) { int eventSize = sizeof(BaseEvent) + nParam * sizeof(uint32_t); eventCounter++; TRC_STREAM_PORT_ALLOCATE_EVENT(largestEventType, event, eventSize); if (event != NULL) { event->base.EventID = (eventID) | PARAM_COUNT(nParam); event->base.EventCount = eventCounter; event->base.TS = prvGetTimestamp32(); /* 32-bit write-pointer for the data argument */ uint32_t* data32 = (uint32_t*) &(event->data[0]); for (i = 0; i < nArgs; i++) { if ((userEvtChannel != NULL) && (i == 0)) { /* First, add the User Event Channel if not NULL */ data32[i] = (uint32_t)userEvtChannel; } else { /* Add data arguments... */ data32[i] = va_arg(vl, uint32_t); } } for (i = 0; i < len; i++) { event->data[nArgs * 4 + i] = str[i]; } event->data[nArgs * 4 + len] = 0; TRC_STREAM_PORT_COMMIT_EVENT(event, eventSize); } } TRACE_EXIT_CRITICAL_SECTION(); } /* Saves a symbol name (task name etc.) in symbol table */ void vTraceSaveSymbol(void *address, const char *name) { uint32_t i = 0; uint32_t foundSlot = SYMBOL_TABLE_BUFFER_SIZE; void *ptr; for (i = 0; i < SYMBOL_TABLE_BUFFER_SIZE; i += SYMBOL_TABLE_SLOT_SIZE) { ptr = *((void**)&symbolTable.pSymbolTableBuffer[i]); if (ptr == 0 && foundSlot == SYMBOL_TABLE_BUFFER_SIZE) { foundSlot = i; } else if (ptr == address) { foundSlot = i; break; } } if (foundSlot != SYMBOL_TABLE_BUFFER_SIZE) { *((uint32_t*)&symbolTable.pSymbolTableBuffer[foundSlot]) = (uint32_t)address; for (i = 0; i < TRC_SYMBOL_MAX_LENGTH; i++) { if (name[i] == 0) break; symbolTable.pSymbolTableBuffer[foundSlot + sizeof(uint32_t) + i] = name[i]; } /* Check the length of "name", if longer than SYMBOL_MAX_LENGTH */ while ((name[i] != 0) && i < 128) { i++; } /* Remember the longest symbol name, for diagnostic purposes */ if (i > LongestSymbolName) { LongestSymbolName = i; } } else { NoRoomForSymbol++; } } /* Deletes a symbol name (task name etc.) from symbol table */ void vTraceDeleteSymbol(void *address) { uint32_t i = 0; void **ptr; for (i = 0; i < SYMBOL_TABLE_BUFFER_SIZE; i += SYMBOL_TABLE_SLOT_SIZE) { ptr = ((void**)&symbolTable.pSymbolTableBuffer[i]); if (*ptr == address) { *ptr = 0; break; } } } /* Saves an object data entry (task base priority) in object data table */ void vTraceSaveObjectData(void *address, uint32_t data) { uint32_t i = 0; uint32_t foundSlot = OBJECT_DATA_TABLE_BUFFER_SIZE; void *ptr; for (i = 0; i < OBJECT_DATA_TABLE_BUFFER_SIZE; i += OBJECT_DATA_SLOT_SIZE) { ptr = *((void**)&objectDataTable.pObjectDataTableBuffer[i]); if (ptr == 0 && foundSlot == OBJECT_DATA_TABLE_BUFFER_SIZE) { foundSlot = i; } else if (ptr == address) { foundSlot = i; break; } } if (foundSlot != OBJECT_DATA_TABLE_BUFFER_SIZE) { *(uint32_t*)&objectDataTable.pObjectDataTableBuffer[foundSlot] = (uint32_t)address; *(uint32_t*)&objectDataTable.pObjectDataTableBuffer[foundSlot + sizeof(uint32_t)] = data; } else { NoRoomForObjectData++; } } /* Removes an object data entry (task base priority) from object data table */ void vTraceDeleteObjectData(void *address) { uint32_t i = 0; void **ptr; for (i = 0; i < OBJECT_DATA_TABLE_BUFFER_SIZE; i += OBJECT_DATA_SLOT_SIZE) { ptr = (void**)&objectDataTable.pObjectDataTableBuffer[i]; if (*ptr == address) { *ptr = 0; break; } } } /* Checks if the provided command is a valid command */ int isValidCommand(TracealyzerCommandType* cmd) { uint16_t checksum = (uint16_t)(0xFFFF - ( cmd->cmdCode + cmd->param1 + cmd->param2 + cmd->param3 + cmd->param4 + cmd->param5)); if (cmd->checksumMSB != (unsigned char)(checksum >> 8)) return 0; if (cmd->checksumLSB != (unsigned char)(checksum & 0xFF)) return 0; if (cmd->cmdCode > CMD_LAST_COMMAND) return 0; return 1; } /* Executed the received command (Start or Stop) */ void processCommand(TracealyzerCommandType* cmd) { switch(cmd->cmdCode) { case CMD_SET_ACTIVE: intSetRecorderEnabled(cmd->param1); break; default: break; } } /* Called on critical errors in the recorder. Stops the recorder! */ void psfError(int errCode) { if (! errorCode) { errorCode = errCode; vTracePrintF(trcWarningChannel, "Error %d. Stopped recorder.", errorCode); intSetRecorderEnabled(0); } } /* Performs timestamping using definitions in trcHardwarePort.h */ static uint32_t prvGetTimestamp32(void) { int ticks = TRACE_GET_OS_TICKS(); return (HWTC_COUNT & 0x00FFFFFF) + ((ticks & 0x000000FF) << 24); } /* Store the Timestamp Config event */ static void vTraceStoreTSConfig(void) { vTraceStoreEvent3( PSF_EVENT_TS_CONFIG, (uint32_t)TRACE_CPU_CLOCK_HZ, (uint32_t)TRACE_TICK_RATE_HZ, (int32_t)HWTC_TYPE); } #endif