/******************************************************************************* | |
* Trace Recorder Library for Tracealyzer v3.0.2 | |
* Percepio AB, www.percepio.com | |
* | |
* trcPagedEventBuffer.c | |
* | |
* Implements a paged buffer that can be used by TCP/IP or custom transfer | |
* methods. | |
* | |
* 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 <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include "trcConfig.h" | |
#include "trcPagedEventBuffer.h" | |
#include "trcPagedEventBufferConfig.h" | |
#include "trcKernelPort.h" | |
uint32_t DroppedEventCounter = 0; // Total number of dropped events (failed allocations) | |
uint32_t TotalBytesRemaining_LowWaterMark = TRC_PAGED_EVENT_BUFFER_PAGE_COUNT * TRC_PAGED_EVENT_BUFFER_PAGE_SIZE; | |
#if (USE_TRACEALYZER_RECORDER == 1) | |
#define PAGE_STATUS_FREE 0 | |
#define PAGE_STATUS_WRITE 1 | |
#define PAGE_STATUS_READ 2 | |
uint32_t TotalBytesRemaining = TRC_PAGED_EVENT_BUFFER_PAGE_COUNT * TRC_PAGED_EVENT_BUFFER_PAGE_SIZE; | |
typedef struct{ | |
uint8_t Status; | |
uint16_t BytesRemaining; | |
char* WritePointer; | |
} PageType; | |
PageType PageInfo[TRC_PAGED_EVENT_BUFFER_PAGE_COUNT]; | |
char* EventBuffer = NULL; | |
static void prvPageReadComplete(int pageIndex); | |
static int prvAllocateBufferPage(int prevPage); | |
static int prvAllocateBufferPage(int prevPage) | |
{ | |
int index; | |
int count = 0; | |
index = (prevPage + 1) % TRC_PAGED_EVENT_BUFFER_PAGE_COUNT; | |
while((PageInfo[index].Status != PAGE_STATUS_FREE) && (count ++ < TRC_PAGED_EVENT_BUFFER_PAGE_COUNT)) | |
{ | |
index = (index + 1) % TRC_PAGED_EVENT_BUFFER_PAGE_COUNT; | |
} | |
if (PageInfo[index].Status == PAGE_STATUS_FREE) | |
{ | |
return index; | |
} | |
return -1; | |
} | |
static void prvPageReadComplete(int pageIndex) | |
{ | |
TRACE_ALLOC_CRITICAL_SECTION(); | |
TRACE_ENTER_CRITICAL_SECTION(); | |
PageInfo[pageIndex].BytesRemaining = TRC_PAGED_EVENT_BUFFER_PAGE_SIZE; | |
PageInfo[pageIndex].WritePointer = &EventBuffer[pageIndex * TRC_PAGED_EVENT_BUFFER_PAGE_SIZE]; | |
PageInfo[pageIndex].Status = PAGE_STATUS_FREE; | |
TotalBytesRemaining += TRC_PAGED_EVENT_BUFFER_PAGE_SIZE; | |
TRACE_EXIT_CRITICAL_SECTION(); | |
} | |
static int prvGetBufferPage(int32_t* bytesUsed) | |
{ | |
static int8_t lastPage = -1; | |
int count = 0; | |
int8_t index = (lastPage + 1) % TRC_PAGED_EVENT_BUFFER_PAGE_COUNT; | |
while((PageInfo[index].Status != PAGE_STATUS_READ) && (count++ < TRC_PAGED_EVENT_BUFFER_PAGE_COUNT)) | |
{ | |
index = (index + 1) % TRC_PAGED_EVENT_BUFFER_PAGE_COUNT; | |
} | |
if (PageInfo[index].Status == PAGE_STATUS_READ) | |
{ | |
*bytesUsed = TRC_PAGED_EVENT_BUFFER_PAGE_SIZE - PageInfo[index].BytesRemaining; | |
lastPage = index; | |
return index; | |
} | |
*bytesUsed = 0; | |
return -1; | |
} | |
/******************************************************************************* | |
int32_t vPagedEventBufferTransfer(int32_t (*writeFunc)(void* data, | |
uint32_t size), | |
int32_t* nofBytes) | |
Transfers one block of trace data, if available for reading. Returns the number | |
of bytes transfered, or a negative error code. If data was transferred (return | |
value > 0), it can be good to call this function again until all data available | |
has been transfered. | |
This function is intended to be called by a periodic task with a suitable | |
delay (e.g. 10-100 ms). | |
Example: | |
TickType_t lastWakeTime = xTaskGetTickCount(); | |
while(1) | |
{ | |
do{ | |
// Transfer all available data | |
status = vPagedEventBufferTransfer(MyWrite, ptrBytes); | |
}while(status > 0); | |
if (status < 0) | |
{ | |
// A negative return value is an error code... | |
} | |
vTraceDelayUntil(lastWakeTime, 50); // 50 ms -> 20 times/sec | |
} | |
Return value: returnvalue of writeFunc (0 == OK) | |
Parameters: | |
- writeFunc | |
Function pointer (example: int32_t write(void* data, uint32_t size)) | |
The function passed as writeFunc should write "size" bytes from "data" to the | |
socket/file/channel, and return a status code where 0 means OK, | |
and any other non-zero value means an error. | |
- int32_t* nofBytes | |
Pointer to an integer assigned the number of bytes that was transfered. | |
*******************************************************************************/ | |
int32_t vPagedEventBufferTransfer(int32_t (*writeFunc)(void* data, uint32_t size, int32_t* ptrBytesWritten), int32_t* nofBytes) | |
{ | |
static int firstTime = 1; | |
int8_t pageToTransfer = -1; | |
pageToTransfer = prvGetBufferPage(nofBytes); | |
if (firstTime) | |
{ | |
firstTime = 0; | |
} | |
if (pageToTransfer > -1) | |
{ | |
if (writeFunc(&EventBuffer[pageToTransfer * TRC_PAGED_EVENT_BUFFER_PAGE_SIZE], *nofBytes, nofBytes) == 0) | |
{ | |
prvPageReadComplete(pageToTransfer); | |
return 0; | |
} | |
else | |
{ | |
return 1; | |
} | |
} | |
return 0; | |
} | |
/******************************************************************************* | |
void* vPagedEventBufferGetWritePointer(int sizeOfEvent) | |
Returns a pointer to an available location in the buffer able to store the | |
requested size. | |
Return value: The pointer. | |
Parameters: | |
- sizeOfEvent | |
The size of the event that is to be placed in the buffer. | |
*******************************************************************************/ | |
void* vPagedEventBufferGetWritePointer(int sizeOfEvent) | |
{ | |
void* ret; | |
static int currentWritePage = -1; | |
if (currentWritePage == -1) | |
{ | |
currentWritePage = prvAllocateBufferPage(currentWritePage); | |
if (currentWritePage == -1) | |
{ | |
DroppedEventCounter++; | |
return NULL; | |
} | |
} | |
if (PageInfo[currentWritePage].BytesRemaining - sizeOfEvent < 0) | |
{ | |
PageInfo[currentWritePage].Status = PAGE_STATUS_READ; | |
TotalBytesRemaining -= PageInfo[currentWritePage].BytesRemaining; // Last trailing bytes | |
if (TotalBytesRemaining < TotalBytesRemaining_LowWaterMark) | |
TotalBytesRemaining_LowWaterMark = TotalBytesRemaining; | |
currentWritePage = prvAllocateBufferPage(currentWritePage); | |
if (currentWritePage == -1) | |
{ | |
DroppedEventCounter++; | |
return NULL; | |
} | |
} | |
ret = PageInfo[currentWritePage].WritePointer; | |
PageInfo[currentWritePage].WritePointer += sizeOfEvent; | |
PageInfo[currentWritePage].BytesRemaining -= sizeOfEvent; | |
TotalBytesRemaining -= sizeOfEvent; | |
if (TotalBytesRemaining < TotalBytesRemaining_LowWaterMark) | |
TotalBytesRemaining_LowWaterMark = TotalBytesRemaining; | |
return ret; | |
} | |
/******************************************************************************* | |
void vPagedEventBufferInit(char* buffer) | |
Assigns the buffer to use and initializes the PageInfo structure. | |
Return value: void | |
Parameters: | |
- buffer | |
Pointer to the buffer location that is dynamically or statically allocated by | |
the caller. | |
*******************************************************************************/ | |
void vPagedEventBufferInit(char* buffer) | |
{ | |
TRACE_ALLOC_CRITICAL_SECTION(); | |
int i; | |
EventBuffer = buffer; | |
TRACE_ENTER_CRITICAL_SECTION(); | |
for (i = 0; i < TRC_PAGED_EVENT_BUFFER_PAGE_COUNT; i++) | |
{ | |
PageInfo[i].BytesRemaining = TRC_PAGED_EVENT_BUFFER_PAGE_SIZE; | |
PageInfo[i].WritePointer = &EventBuffer[i * TRC_PAGED_EVENT_BUFFER_PAGE_SIZE]; | |
PageInfo[i].Status = PAGE_STATUS_FREE; | |
} | |
TRACE_EXIT_CRITICAL_SECTION(); | |
} | |
#endif | |