diff options
author | Roland Veloz <rveloz@us.ibm.com> | 2018-05-30 11:18:42 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2018-06-19 17:24:07 -0400 |
commit | 0189e34d3bbc3ce77e1242c94750f99ef0d58d90 (patch) | |
tree | 943b04e2a17cf81bd902b99ce862a7b41e69a027 /src/usr/trace | |
parent | 1ec6201b896c5cbc22fef03df1a063faa8cd6411 (diff) | |
download | talos-hostboot-0189e34d3bbc3ce77e1242c94750f99ef0d58d90.tar.gz talos-hostboot-0189e34d3bbc3ce77e1242c94750f99ef0d58d90.zip |
Create a utility to add/remove entries from a link list within a given buffer
This buffer is designed to be used in a runtime environment.
I am laying the ground work for an over arching story that will
save traces in a buffer for future consumption.
For this commit, I have created the methods necessary to add, remove and find
available space (for an Entry) in a buffer bound region of memory using a
circular, doubly linked list.
Change-Id: I259bb0f6051611a17b7b919bf026919ffdb12eb1
RTC: 191303
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/59575
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Prachi Gupta <pragupta@us.ibm.com>
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr/trace')
-rw-r--r-- | src/usr/trace/runtime/makefile | 4 | ||||
-rw-r--r-- | src/usr/trace/runtime/rt_rsvdtracebuffer.C | 413 | ||||
-rw-r--r-- | src/usr/trace/runtime/rt_rsvdtracebuffer.H | 461 | ||||
-rw-r--r-- | src/usr/trace/runtime/rt_rsvdtracebufservice.C | 214 | ||||
-rw-r--r-- | src/usr/trace/runtime/rt_rsvdtracebufservice.H | 122 | ||||
-rw-r--r-- | src/usr/trace/runtime/test/makefile | 2 | ||||
-rw-r--r-- | src/usr/trace/runtime/test/testrsvdtracebuf.H | 1593 |
7 files changed, 2808 insertions, 1 deletions
diff --git a/src/usr/trace/runtime/makefile b/src/usr/trace/runtime/makefile index 8974e15c0..7e23a1e71 100644 --- a/src/usr/trace/runtime/makefile +++ b/src/usr/trace/runtime/makefile @@ -26,7 +26,7 @@ HOSTBOOT_RUNTIME = 1 ROOTPATH = ../../../.. MODULE = trace_rt -SUBDIRS += test.d +EXTRAINCDIR +=${ROOTPATH}/src/usr OBJS += interface.o OBJS += assert.o @@ -43,4 +43,6 @@ OBJS += rt_rsvdtracebufservice.o VPATH += .. VPATH += ../daemon +SUBDIRS += test.d + include $(ROOTPATH)/config.mk diff --git a/src/usr/trace/runtime/rt_rsvdtracebuffer.C b/src/usr/trace/runtime/rt_rsvdtracebuffer.C new file mode 100644 index 000000000..b9d21b774 --- /dev/null +++ b/src/usr/trace/runtime/rt_rsvdtracebuffer.C @@ -0,0 +1,413 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/runtime/rt_rsvdtracebuffer.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2017,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include "rt_rsvdtracebuffer.H" +#include <string.h> // memset + +namespace TRACE +{ +/** + * ctor + */ +RsvdTraceBuffer::RsvdTraceBuffer() +{ + // The buffer is not valid - there is no actual/real buffer to point to. + // With no actual/real buffer to point to, all internal data is NULL/0. + setBufferValidity(false); + clearPtrToHead(); + setBeginningBoundary(nullptr); + setEndingBoundary(nullptr); +} + +/** + * init + */ +void RsvdTraceBuffer::init(uint32_t i_bufferSize, + uintptr_t i_addressToBuffer, + uintptr_t* i_addressToHead) +{ + // If buffer is not already initilaized and incoming data is legit + if ( (false == isBufferValid()) && + (i_bufferSize > 0 ) && + (i_addressToBuffer > 0) && + (nullptr != i_addressToHead) ) + { + setBeginningBoundary(convertToCharPointer(i_addressToBuffer)); + setEndingBoundary(convertToCharPointer(i_addressToBuffer + + i_bufferSize - 1)); + setListHeadPtr(i_addressToHead); + + // Now that there is an actual/real buffer to point to, the buffer is + // valid, although it may/may not have any entries associated with it. + setBufferValidity(true); + } +} + +/** + * insertEntry + */ +Entry* RsvdTraceBuffer::insertEntry(uint32_t i_dataSize) +{ + // Create a handle to an Entry + Entry* l_entry(nullptr); + + // Before continuing, make sure the buffer is valid + if (isBufferValid()) + { + char* l_availableAddress(nullptr); + + // Bump up the needed size to include the entry itself and + // necessary alignment. + // (Alignment is needed so that Entry's members can be atomically + // updated). + uint32_t l_entrySize = getAlignedSizeOfEntry(i_dataSize); + if (makeSpaceForEntry(l_entrySize, l_availableAddress) && l_availableAddress) + { + // Set entry if space was created and an avilable address is returned + l_entry = reinterpret_cast<Entry*>(l_availableAddress); + + setListTail(l_entry); + } + } + + return l_entry; +} + +/** + * makeSpaceForEntry + */ +uint32_t RsvdTraceBuffer::makeSpaceForEntry(uint32_t i_spaceNeeded, + char* &o_availableAddress) +{ + o_availableAddress = nullptr; + uint32_t l_spaceAvailable = 0; + + // Only look for space if requested space is less or equal to buffer size + if (i_spaceNeeded <= getBufferSize()) + { + l_spaceAvailable = getAvailableSpace(i_spaceNeeded, o_availableAddress); + + // Keep requesting for space until we get the space that is asked for + while (l_spaceAvailable < i_spaceNeeded) + { + // If we can't remove any entries, then we exhausted all efforts + // Should not happen, because the space requested should be less + // than or equal to buffer size + if (!removeOldestEntry()) + { + l_spaceAvailable = 0; + break; + } + + l_spaceAvailable = getAvailableSpace(i_spaceNeeded, + o_availableAddress); + } + } + + return l_spaceAvailable; +} + +/** + * getAvailableSpace + */ +uint32_t RsvdTraceBuffer::getAvailableSpace(uint32_t i_spaceNeeded, + char* &o_availableAddress) +{ + o_availableAddress = nullptr; + uint32_t l_availableSpace(0); + + // If the list is empty, then the full buffer is available + if (isListEmpty()) + { + l_availableSpace = getBufferSize(); + o_availableAddress = iv_bufferBeginningBoundary; + } + // The list is not empty; must find available space + else + { + // Cache some useful data for easy calculations later on + uintptr_t l_bufferBeginningBoundary = getAddressOfPtr(iv_bufferBeginningBoundary); + uintptr_t l_bufferEndingBoundary = getAddressOfPtr(iv_bufferEndingBoundary); + Entry* l_head = getListHead(); + uintptr_t l_headAddr = getAddressOfPtr(l_head); + uintptr_t l_tailAddrEnd = getEndingAddressOfEntry(l_head->prev); + + // If address of the tail is greater or equal to the head then the tail + // will be at the end of the buffer or, in other words, ahead of the head. + if (l_tailAddrEnd >= l_headAddr) + { + // Get available space between the tail and buffer ending boundary + size_t l_spaceAtEnd = l_bufferEndingBoundary - l_tailAddrEnd; + // Get available space between the buffer beginning boundary and head + size_t l_spaceAtBeginning = l_headAddr - l_bufferBeginningBoundary; + + // If the space available at end of buffer is enough to satisfy the + // space needed, then return that value else return the space + // available at the beginning of the buffer. If the space at the + // end does not have enough of the needed space, then space will + // ultimately be made at the beginning of the + // + // Right now, you are probably thinking, what if I only need 5 free + // spaces and if the end has 10 available and the beginning has 7 + // available, why not return the space at the beginning, the + // minimum needed to satisfy our needs. I believe we still would + // want to "fill out" the end, before starting with beginning. + // + // Side note: We want to return contiguous memory only. Although + // l_spaceAtEnd + l_spaceAtBeginning may satisfy the space needed, + // it is not contiguous. + if (l_spaceAtEnd >= i_spaceNeeded) + { + // There is more available space at the end of the buffer + l_availableSpace = l_spaceAtEnd; + o_availableAddress = convertToCharPointer(l_tailAddrEnd + 1); + } + // Just return the space available at beginning and hopefully + // that will satisfy our needs + else + { + // There is more available space at the beginning of the buffer + l_availableSpace = l_spaceAtBeginning; + o_availableAddress = iv_bufferBeginningBoundary; + } + } + // The tail is behind the head in the buffer + else + { + // Get available space between the head and tail + l_availableSpace = l_headAddr - l_tailAddrEnd + 1; + o_availableAddress = convertToCharPointer(l_tailAddrEnd + 1); + } + } + return l_availableSpace; +} + +/** + * removeOldestEntry + */ +bool RsvdTraceBuffer::removeOldestEntry() +{ + bool l_oldestEntryRemoved(false); + + // If the list is not empty, then remove oldest entry - the head + if (!isListEmpty()) + { + // Get a handle to the head + Entry* l_head = getListHead(); + + // Is there only one entry? + if (l_head->next == l_head) + { + // Yes, just set the head to nullptr and we are done + l_head = nullptr; + } + // There is more than one entry, so remove head (the oldest entry) + else + { + // Point head's next entry to head's previous entry + l_head->next->prev = l_head->prev; + // Point head's previous entry to head's next entry + l_head->prev->next = l_head->next; + // Now set head to the next entry + l_head = l_head->next; + } + + // Update the head of the list + setListHead(l_head); + l_oldestEntryRemoved = true; + } + + return l_oldestEntryRemoved; +} + +/** + * getTrace + */ +uint32_t RsvdTraceBuffer::getTrace(void* o_data, uint32_t i_dataSize) const +{ + uint32_t l_sizeOfBufferExtracted(0); + + // Before continuing, make sure the buffer is valid + if (isBufferValid()) + { + if ((nullptr == o_data) || (0 == i_dataSize)) + { + l_sizeOfBufferExtracted = getAggregateSizeOfEntries(); + } + else + { + l_sizeOfBufferExtracted = getTraceEntries(o_data, i_dataSize); + } + } + + return l_sizeOfBufferExtracted; +} + +/** + * aggregateSizeOfEntries + */ +uint32_t RsvdTraceBuffer::getAggregateSizeOfEntries() const +{ + uint32_t l_aggregatedSize(0); + + // Get a handle to the head + Entry* l_head = getListHead(); + + // Make sure the list is not null + if (l_head) + { + Entry* l_entry = l_head; + do + { + // Need to add to the size, the size of an uint32_t. The uint32_t + // will hold the size of the data that is to be returned along + // with the returned data. This is why it is added. + l_aggregatedSize += l_entry->size + sizeof(uint32_t); + l_entry = l_entry->next; + } while (l_entry != l_head); + } + + // Add size of trace_buf_head_t to get the true size requirements + return (l_aggregatedSize + sizeof(trace_buf_head_t)); +} + +/** + * getTraceEntries + */ +uint32_t RsvdTraceBuffer::getTraceEntries(void* o_data, uint32_t i_dataSize) const +{ + uint32_t l_sizeOfEntries(0); + + // Before proceeding, make sure the incoming data is valid + if ((nullptr != o_data) && + (i_dataSize >= sizeof(trace_buf_head_t)) ) + { + // Get a useful "trace buffer head" handle to the data buffer passed in + trace_buf_head_t* l_header =reinterpret_cast<trace_buf_head_t*>(o_data); + + // Now that we have an easy handle to the data, let's clear it for now + memset(l_header, '\0', sizeof(trace_buf_head_t)); + + // Now populate the trace buffer header with some useful info + l_header->ver = TRACE_BUF_VERSION; + l_header->hdr_len = l_header->size = sizeof(trace_buf_head_t); + l_header->time_flg = TRACE_TIME_REAL; + strncpy(l_header->comp, "RSVD_MEM_TRACE", TRAC_COMP_SIZE); + l_header->endian_flg = 'B'; // Big Endian. + + // Get a handle to the head + Entry* l_head = getListHead(); + + // Extract the trace info from this class' internal buffer + // If the list is not empty and have data then extract the trace info + if (l_head) + { + // Keep a tally of the size of the data that can be copied over + uint32_t l_totalSize(l_head->size); + // Keep a tally of the number of entries that can be extracted + uint32_t l_entriesToExtract(0); + // The entry size as data type uint32_t; for code up keep + uint32_t l_entrySize(0); + // Get a handle on the last entry on the list + Entry* l_entry = l_head->prev; + + // Calculate the number of entries that can be stuffed into data buffer + // starting with newest entry (tail) to oldest entry (head) + do + { + // Calculate the size: add the size of the data, that will be + // copied over, plus the size of the type of the entry size, + // that will hold the size of the data being copied over. + if ((l_totalSize + l_entry->size + sizeof(l_entrySize)) <= i_dataSize) + { + l_totalSize += l_entry->size + sizeof(l_entrySize); + ++l_entriesToExtract; + } + else // Can't retrieve this entry; it breaks the size limitation + { + break; + } + + l_entry = l_entry->prev; + } while (l_entry != l_head->prev); + + // Get a useful "char *" handle to the data buffer passed in, + // for easy data injection + char* l_data = reinterpret_cast<char*>(o_data); + + // Retrieve the data, going forwards in the list: Want to retrieve + // the entries in chronological order + // Currently pointing one entry behind the starting entry (starting + // entry, meaning the first entry to start gathering data from) + while (l_entriesToExtract) + { + // Get next entry + l_entry = l_entry->next; + + // Copy entry data. + memcpy(&l_data[l_header->size], l_entry->data, l_entry->size); + l_header->size += l_entry->size; + + // Copy entry size. + l_entrySize = l_entry->size + sizeof(l_entrySize); + memcpy(&l_data[l_header->size], &l_entrySize, sizeof(l_entrySize)); + l_header->size += sizeof(l_entrySize); + + ++l_header->te_count; + --l_entriesToExtract; + } // end while (l_entriesToExtract) + } // end if (!isListEmpty()) + + // Update the size of the entries retrieved and the + // next free memory location in header trace buffer + l_sizeOfEntries = l_header->next_free = l_header->size; + } + + return l_sizeOfEntries; +} + +/** + * getNumberOfEntries + */ +uint32_t RsvdTraceBuffer::RsvdTraceBuffer::getNumberOfEntries() const +{ + uint32_t l_numberOfEntries(0); + + // If the list is not empty, count the entries + if (!isListEmpty()) + { + // Get a handle to the head + Entry* l_head = getListHead(); + do + { + ++l_numberOfEntries; + l_head = l_head->next; + } while (l_head != getListHead()); + } + + return l_numberOfEntries; +} + +} // end namespace TRACE diff --git a/src/usr/trace/runtime/rt_rsvdtracebuffer.H b/src/usr/trace/runtime/rt_rsvdtracebuffer.H new file mode 100644 index 000000000..24c9204cf --- /dev/null +++ b/src/usr/trace/runtime/rt_rsvdtracebuffer.H @@ -0,0 +1,461 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/runtime/rt_rsvdtracebuffer.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RSVD_TRACE_BUFFER_H +#define __RSVD_TRACE_BUFFER_H + +/** @file rt_rsvdtracebuffer.H + * Declarations for the RsvdTraceBuffer (Reserved Trace Buffer) class. + */ + +#include <stdint.h> // uint32_t +#include <trace/entry.H> // Entry +#include <util/align.H> // ALIGN_8 + +// Forward declaration of the test suite +class RsvdTraceBuffTestSuite; + +namespace TRACE +{ + + /** @class RsvdTraceBuffer + * + * @brief Class to manage the Reserved Trace Buffer + * + * This is a utility class to manage the buffer - looking for space + * for an entry, adding entries and removing entries. + * + */ + class RsvdTraceBuffer + { + public: + + /** Constructor. + */ + RsvdTraceBuffer(); + + /** Initializes the buffer, if previously not done. + * + * @param[in] i_bufferSize - Size of the buffer. + * + * @param[in] i_addressToBuffer - Where the buffer begins + * + * @param[in] i_addressToHead - A pointer to a ponter to the first + * Entry, cannot be a nullptr + */ + void init(uint32_t i_bufferSize, + uintptr_t i_addressToBuffer, + uintptr_t* i_addressToHead); + + /** @brief This function will insert an Entry to the buffer + * + * @param[in] i_dataSize - The size of the contiguous piece + * of memory caller desires + * + * @return A valid Entry pointer if space found for an Entry; + * nullptr otherwise + */ + Entry* insertEntry(uint32_t i_dataSize); + + /** @brief Extract the trace info or return aggregated size of the + * traces + * + * @algorithm If o_data is null or i_dataSize is 0, then this + * method will return all the trace sizes, this class + * contains, aggregated together. + * If o_data is valid and the i_size is less than + * the size of trace_buf_head_t, then 0 is returned. + * If o_data is valid and the i_size is greater than + * the size of trace_buf_head_t, then as many trace + * entries will be returned in the o_data buffer that + * i_size will allow. + * + * @param[in] o_data - if not null, the buffer area to copy + * trace data into + * + * @param[out] i_dataSize - if not 0, the size of the buffer area, + * which dictates how many trace entries' + * payload (or data the entry contains) + * that can be copied + * + * @return the size of the trace data being returned, or the + * aggregated size of the traces, or 0 + */ + uint32_t getTrace(void* o_data, uint32_t i_dataSize) const; + + + /** @brief Return the state of buffer + * + * @return true if buffer has been initialized properly, + * false otherwise + * + */ + bool isBufferValid() const + { return iv_isBufferValid; } + + /** @brief Clears the buffer of all entries + */ + void clearBuffer() + { setListHead(reinterpret_cast<TRACE::Entry*>(NULL)); } + + /** @brief Retrieve the number of entries in buffer + */ + uint32_t getNumberOfEntries() const; + + private: + /** @brief This function will find a contiguous piece of memory that + * is large enough in size to accommodate the space needed. + * If not enough free contiguous memory exists to accommodate + * the space needed, then space will be created that will be + * large enough for the space needed with one caveat - if + * the space requested is greater than the size of the + * buffer, then no attempt will be made to accommodate the + * space needed but instead 0 will be returned and + * o_availableAddress will be set to nullptr. + * + * @algorithm Call the method getAvailableSpace (below) to get + * the largest contiguous piece of memory, whether it + * satisfies our needs or not. If it does satisfy our + * needs, then return the size of the space found and a + * pointer to the beginning of that space. If it does + * not satisfy our spatial needs, then remove the head + * entry from the list and check again. We remove the + * head because the head entry will be the oldest entry + * (time wise). Repeat until we get the space needed + * or until all entries are removed. Unless the space + * requested is not larger in size to the buffer size, + * then an available space will eventually be returned. + * + * @param[in] i_spaceNeeded - @see insertEntry::i_dataSize above + * + * @param[out] o_availableAddress - A pointer to the contiguous + * piece of memory found that satisfies the caller's + * request. If not found, then nullptr + * + * @return the size of the space found/created; 0 if none + * found/created + */ + uint32_t makeSpaceForEntry(uint32_t i_spaceNeeded, + char* &o_availableAddress); + + /** @brief Returns a contiguous piece of memory that will satisfy + * the space that is needed if large enough space can be + * had, else returns the size of the largest contiguous + * piece of memory. + * + * @algorithm There are three cases to consider: + * 1) Tail is in front of the head (address wise, not + * logical wise) and the space between tail and end + * of buffer satisfies our spatial needs then return the + * space between tail and buffer end. + * Example 1: + * --- Beginning of Buffer End of Buffer --- + * | | + * V V + * --------------------------------------------------------- + * | < - 10 bytes -> | Head | .....| Tail | <- 20 bytes -> | + * --------------------------------------------------------- + * scenario 1: Contigous space desired: 15 bytes + * Return the 20 bytes after the Tail + * scenario 2: Contiguous space desired: 10 bytes + * Return the 20 bytes after the Tail + * Here we can return the 10 bytes or + * 20 bytes - both satisfy the condition. + * In this case, return the 20 bytes to + * take full advantage of the buffer. + * + * 2) Tail is in front of the head and case 1 above cannot + * be satisfied then return the space between the + * beginning of buffer and head. + * Example 2.1: + * --- Beginning of Buffer End of Buffer --- + * | | + * V V + * --------------------------------------------------------- + * | < - 30 bytes -> | Head | .....| Tail | <- 15 bytes -> | + * --------------------------------------------------------- + * scenario 1: Contiguous space desired: 25 bytes + * Return the 30 bytes before the Head + * scenario 2: Contiguous space desired: 40 bytes + * Return the 30 bytes before the Head + * + * Example 2.2: + * --- Beginning of Buffer End of Buffer --- + * | | + * V V + * --------------------------------------------------------- + * | < - 5 bytes -> | Head | .....| Tail | <- 15 bytes -> | + * --------------------------------------------------------- + * scenario 1: Contiguous space desired: 20 bytes + * Return the 5 bytes before the Head + * The reason for returning 5 bytes, even + * though 15 bytes is greater is because, + * ultimately space will be created at + * the beginning of the buffer. + * + * 3) Tail is behind head, then simply return the + * available space between the two. + * Example 3: + * --- Beginning of Buffer End of Buffer --- + * | | + * V V + * --------------------------------------------------------- + * | .... | Tail | < - 30 bytes -> | Head | .... + * --------------------------------------------------------- + * Case 1: Contigous space desired: 25 bytes + * Return the 30 bytes between Tail and Head. + * Case 2: Contigous space desired: 40 bytes + * Return the 30 bytes between Tail and Head. + * + * @param[in] i_spaceNeeded - @see insertEntry::i_dataSize above + * + * @param[out] o_availableAddress - @see makeSpaceForEntry above + * + * @return The minimum size of the space found that meets the + * requested space needed; or the largest size that comes + * close to meeting the space needed + * + */ + uint32_t getAvailableSpace(uint32_t i_spaceNeeded, + char* &o_availableAddress); + + /** @brief Remove the head of the list, because the head is the + * oldest entry + * + * @return Returns true if oldest entry is removed; false otherwise + */ + bool removeOldestEntry(); + + /** @brief This will aggregate all the entries' data size and + * return it + * + * @return Returns all the entries' data size plus the size of + * an uint32_t for each one. + */ + uint32_t getAggregateSizeOfEntries() const; + + /** @brief This will return as many data entries that can be + * accommodated by size + * + * @param[out] o_data - the buffer area to copy trace data into + * + * @param[in] i_dataSize - the size of the buffer area, which + * dictates how many trace data can be + * copied + * + * @return Returns all the entries' data size plus the size of + * an uint32_t for each one, that can be accommodated + */ + uint32_t getTraceEntries(void* o_data, uint32_t i_dataSize) const; + + + /** @brief Return true if list empty + * + * @return true if list empty; false otherwise + */ + bool isListEmpty() const + { return (nullptr == getListHead()); } + + /** @brief Return the address of a pointer + * + * @param[in] i_entry - a pointer to data type; + * void pointer for our purposes + * + * @return The address of the supplied pointer + * + * @pre i_entry is NOT a nullptr + */ + uintptr_t getAddressOfPtr(const void *i_entry) const + { return reinterpret_cast<uintptr_t>(i_entry); }; + + /** @brief Get the address at the end of an Entry, which is the + * size of the Entry plus any padding for memory alignment + * + * @param[in] i_entry - a pointer to an Entry data type + * + * @return The address at the end of an Entry + * + * @pre i_entry is NOT a nullptr + */ + uintptr_t getEndingAddressOfEntry(const Entry *i_entry) const + { return (getAddressOfPtr(i_entry) + + getAlignedSizeOfEntry(i_entry->size) -1); }; + + /** @brief Converts an address to a character pointer + * + * @param[in] i_addressOfPtr - a legitimate address + * + * @return A character pointer to the address supplied + * + * @pre i_addressOfPtr is a a legitimate address of a memory + * location + */ + char* convertToCharPointer(uintptr_t i_addressOfPtr) const + { return reinterpret_cast<char*>(i_addressOfPtr); }; + + /** @brief Save away the beginning boundary of the buffer + * + * @param[in] i_bufferBeginningBoundary - a pointer to the + * beginning of the buffer + * + */ + void setBeginningBoundary(char* i_bufferBeginningBoundary) + { iv_bufferBeginningBoundary = i_bufferBeginningBoundary; }; + + /** @brief Save away the ending boundary of the buffer + * + * @param[in] i_bufferEndingBoundary - a pointer to the + * end of the buffer + * + */ + void setEndingBoundary(char * i_bufferEndingBoundary) + { iv_bufferEndingBoundary = i_bufferEndingBoundary; } + + /** @brief Set the head of the list + * + * @param[in] i_addressToHead - pointer to an area outside + * the controlled buffer that stays updated with + * the head of the list; OK to be a nullptr + * + */ + void setListHeadPtr(uintptr_t* i_addressToHead) + { iv_ptrToHead = i_addressToHead; }; + + /** @brief Set the head of the list + * + * @param[in] i_head - a pointer to an Entry data type; + * OK to be a nullptr + * + */ + void setListHead(Entry* i_entry) + { + if (nullptr != i_entry) + { + i_entry->next = i_entry->prev = i_entry; + } + // Sanity check, make sure the pointer to head is valid + if (nullptr != iv_ptrToHead) + { + *iv_ptrToHead = getAddressOfPtr(i_entry); + } + }; + + /** @brief Get the head of the list + * + * @return return the head of the list as an Entry pointer + */ + Entry* getListHead() const + { + Entry* l_entry = nullptr; + // Make sure the pointer is valid before de-referencing it + if (nullptr != iv_ptrToHead) + { + l_entry = reinterpret_cast<Entry*>(*iv_ptrToHead); + }; + return l_entry; + } + + /** @brief Append an entry to the end of the list + * + * @param[in] i_tail - a pointer to an Entry data type; + * OK to be a nullptr + * + */ + void setListTail(Entry* i_newEntry) + { + Entry* l_head = getListHead(); + + // If there is a head and user passed in a legitimate tail + // then add the tail at the end of the list + if (l_head && i_newEntry) + { + // Get a handle to old tail + Entry* l_tail = l_head->prev; + // Point new entry's (new tail) next entry to head + i_newEntry->next = l_head; + // Point new entry's (new tail) previous entry to tail (old tail) + i_newEntry->prev = l_tail; + // Point head's previous to the new entry (new tail) + l_head->prev = i_newEntry; + // Point tail (old tail) to the new entry (new tail) + l_tail->next = i_newEntry; + } + // Else, head is a nullptr and user passed in a + // legitimate new Entry, then set head to new Entry + else if (i_newEntry) + { + setListHead(i_newEntry); + } + // else tail is not legit, so let's get out of here, + // nothing to do + } + + + /** @brief Get the size of the buffer; the buffer that is available + * for Entries. + * + * @return Size of buffer + */ + uint32_t getBufferSize() const + { return (iv_bufferEndingBoundary - + iv_bufferBeginningBoundary + 1); }; + + /** @brief Get the size of an Entry plus the bytes needed to memory + * align the Entry. + * + * @param[in] i_dataSize - the size of the data that extends beyond + * the Entry data type + * + * @return The total size of Entry; includes size of the Entry + * itself, size of the data that is associated with Entry + * and extras size so as to be properly aligned + * + */ + uint32_t getAlignedSizeOfEntry(uint32_t i_dataSize) const + { return ALIGN_8(sizeof(Entry) + i_dataSize); } + + /** @brief Set the buffer state + * + * @param[in] i_state - the new state of the buffer; false or true + * + */ + void setBufferValidity(bool i_state) + { iv_isBufferValid = i_state; } + + /** @brief Clears the the pointer that points to the head + */ + void clearPtrToHead() + { iv_ptrToHead = nullptr; } + + char *iv_bufferBeginningBoundary; //< Pointer to beginning of buffer + char *iv_bufferEndingBoundary; //< Pointer to end of buffer + uintptr_t* iv_ptrToHead; //< Pointer to oldest Entry (time wise) + bool iv_isBufferValid; //< Indicates an initialized buffer + + // For testing purposes only + friend class ::RsvdTraceBuffTestSuite; +}; +} + +#endif diff --git a/src/usr/trace/runtime/rt_rsvdtracebufservice.C b/src/usr/trace/runtime/rt_rsvdtracebufservice.C new file mode 100644 index 000000000..44d76096f --- /dev/null +++ b/src/usr/trace/runtime/rt_rsvdtracebufservice.C @@ -0,0 +1,214 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/runtime/rt_rsvdtracebufservice.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2013,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include "rt_rsvdtracebufservice.H" +#include <trace/compdesc.H> // ComponentDes +#include <trace/entry.H> // Entry +#include <util/runtime/util_rt.H> // hb_get_rt_rsvd_mem +#include <errl/errlmanager.H> // errlCommit +#include <runtime/interface.h> // g_hostInterfaces, postInitCalls_t +#include <runtime/runtime_reasoncodes.H> // RUNTIME::MOD_INIT_RT_RES_MEM_TRACE_BUF + +namespace TRACE +{ + +/** + * ctor + */ +RsvdTraceBufService::RsvdTraceBufService( ) +: iv_errl(nullptr) +{ +} + +/** + * init + */ +void RsvdTraceBufService::init() +{ + g_hostInterfaces->puts( " >> RsvdTraceBufService::init\n"); + + // Get the size and location of the reserved trace buffer section + uint64_t l_bufferSize(0); + uint64_t l_reservedMemory = hb_get_rt_rsvd_mem(Util::HBRT_MEM_LABEL_TRACEBUF, + 0, l_bufferSize); + + // The first part of buffer is where the head of a link list will reside. + // It needs to reside here so if we go down in flames, we can pick + // up where we left off + if ( (l_reservedMemory != 0) && (l_bufferSize > sizeof(uintptr_t)) ) + { + // Get a pointer to where the head of the data is + uintptr_t *l_addressToHead = + reinterpret_cast<uintptr_t *>(l_reservedMemory); + + // The RsvdTraceBuffer is only concerned with the part of the buffer + // that it will maintain. Therefore, send the buffer size minus the + // size of an uintptr_t which is set aside for the first entry of the + // buffer. Then add the size of an uintptr_t to the l_reservedMemory + // (the beginning address of buffer) to get the beginning address of + // which the RsvdTraceBuffer will be responsible for. + iv_rsvdTraceBuffer.init(l_bufferSize - sizeof(uintptr_t), + l_reservedMemory + sizeof(uintptr_t), + l_addressToHead); + + // If the data is not NULL, then retrieve crashed data + // I want NULL in this case, not nullptr; *l_addressToHead is an int. + // If I use nullptr; compiler complains + if (*l_addressToHead != NULL) + { + retrieveDataFromLastCrash(); + } + + // After gathering trace info from previous crash, clear buffer data + iv_rsvdTraceBuffer.clearBuffer(); + } + + g_hostInterfaces->puts(" << RsvdTraceBufService::init\n"); +} + +/** + * retrieveDataFromLastCrash + */ +void RsvdTraceBufService::retrieveDataFromLastCrash() +{ + g_hostInterfaces->puts( + " >> RsvdTraceBufService::retrieveDataFromLastCrash\n"); + + iv_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_INFORMATIONAL, + RUNTIME::MOD_INIT_RT_RES_MEM_TRACE_BUF, + RUNTIME::RC_RT_RES_TRACE_BUF_DUMPED, + 0,0,true); + + // Note: Set the iv_rsvdTraceBuffer before calling collectTrace() + iv_errl->collectTrace("RSVD_MEM_TRACE",0); + + // Can not commit the errl until the ErrlManager gets initialized. + // Committing will be done in commitRsvdMemTraceErrl() after + // ErrlManager gets initialized + g_hostInterfaces->puts( + " << RsvdTraceBufService::retrieveDataFromLastCrash\n"); +} + + +/** + * commitRsvdMemTraceErrl + */ +bool RsvdTraceBufService::commitRsvdMemTraceErrl() +{ + g_hostInterfaces->puts(" >> RsvdTraceBufService::commitRsvdMemTraceErrl\n"); + + bool l_errlCommitted = false; + + // Commit the iv_errl, if valid + if (iv_errl) + { + errlCommit(iv_errl, RUNTIME_COMP_ID); + l_errlCommitted = true; + } + + g_hostInterfaces->puts(" << RsvdTraceBufService::commitRsvdMemTraceErrl\n"); + + return l_errlCommitted; +} + +/** + * writeEntry + */ +void RsvdTraceBufService::writeEntry(ComponentDesc* i_componentDesc, + char* i_data, + uint32_t i_dataSize) +{ + // Sanity check, make sure user passed in legit data + if ((nullptr != i_componentDesc) && (nullptr != i_data) && (i_dataSize > 0)) + { + // Add an entry into the reserved trace buffer + Entry* l_entry = iv_rsvdTraceBuffer.insertEntry(i_dataSize); + if (nullptr != l_entry) + { + // Assign the component description and entry size. + l_entry->comp = i_componentDesc; + l_entry->size = i_dataSize; + + //copy the contents in to the entry's data section + memcpy(&l_entry->data[0], i_data, i_dataSize); + + // "Commit" entry to buffer. + l_entry->committed = 1; + } + } +} + +/** + * getBuffer + */ +uint32_t RsvdTraceBufService::getBuffer(void* o_data, uint32_t i_dataSize) const +{ + return iv_rsvdTraceBuffer.getTrace(o_data, i_dataSize); +} + +} //end of namespace + +/** + * initRsvdTraceBufService + */ +void initRsvdTraceBufService() +{ + g_hostInterfaces->puts(" >> RsvdTraceBufService::initRsvdTraceBufService\n"); + + //get the reserved memory and initialize + Singleton<TRACE::RsvdTraceBufService>::instance().init(); + + g_hostInterfaces->puts(" << RsvdTraceBufService::initRsvdTraceBufService\n"); + +} + +/** + * commitRsvdTraceBufErrl + */ +void commitRsvdTraceBufErrl() +{ + g_hostInterfaces->puts(" >> RsvdTraceBufService::commitRsvdTraceBufErrl\n"); + + // Commit the errl with traces from previous boot, if buffer is valid + Singleton<TRACE::RsvdTraceBufService>::instance().commitRsvdMemTraceErrl(); + + g_hostInterfaces->puts(" << RsvdTraceBufService::commitRsvdTraceBufErrl\n"); +} + +struct register_initRsvdTraceBufService +{ + register_initRsvdTraceBufService() + { + // Register interface for Host to call + postInitCalls_t * rt_post = getPostInitCalls(); + rt_post->callInitRsvdTraceBufService = &initRsvdTraceBufService; + rt_post->callCommitRsvdTraceBufErrl = &commitRsvdTraceBufErrl; + } +}; + + +//create a global object to force initialize the Service & RsvdTraceBufService objects +register_initRsvdTraceBufService g_register_initRsvdTraceBufService; + diff --git a/src/usr/trace/runtime/rt_rsvdtracebufservice.H b/src/usr/trace/runtime/rt_rsvdtracebufservice.H new file mode 100644 index 000000000..727ab8dae --- /dev/null +++ b/src/usr/trace/runtime/rt_rsvdtracebufservice.H @@ -0,0 +1,122 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/runtime/rt_rsvdtracebufservice.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RSVD_TRACE_SERVICE_H +#define __RSVD_TRACE_SERVICE_H + +/** @file rt_rsvdtracebufservice.H + * + * @brief class with functions to write HBRT traces in to Reserved Trace Buffer + * + * In the constructor, this will get the reserved memory section through + * hb_get_rt_rsvd_mem(Util::HBRT_MEM_LABEL_TRACEBUF) and uses the buffer + * to write the HBRT traces of all components serially. + * Writing to Reserved Trace Buffer is serialized in this class via Mutex + * + * Reserved Trace Buffer is a wrappable circular buffer, oldest entries + * are deleted to write the new trace entries + * + */ + +#include "rt_rsvdtracebuffer.H" // RsvdTraceBuffer +#include <errl/errlentry.H> // errlHndl_t + +// Forward declaration of the test suite +class RsvdTraceBuffTestSuite; + +namespace TRACE +{ + // Forward declarations + class ComponentDesc; + + /** @class RsvdTraceBufService + * + * @brief Front-end interfaces for reserved trace buffer. + * + * There should be a single instance(a singleton) of this class with + * delayed construction. Constructed when needed not at start up. + */ + class RsvdTraceBufService + { + public: + /** Default constructor */ + RsvdTraceBufService(); + + /** Initializes the buffer, if previously not done.*/ + void init(); + + /** Commits the iv_errl with Trace from previous boot, + * if created in init() + * + * @return true if iv_errl is valid, otherwise false. + */ + bool commitRsvdMemTraceErrl(); + + /** @brief Write a Normal/Binary entry to a trace buffer + * + * @param[in] i_componentDesc - Component Descriptor to write to. + * + * @param[in] i_data - pointer to the FSP formatted trace data. + * @param[in] i_size - size of the data + */ + void writeEntry(ComponentDesc* i_componentDesc, + char* i_data, + uint32_t i_dataSize); + + + /** @brief Extracts Reserved Trace buffer. + * + * @param[out] o_data - Data buffer to copy to. + * @param[in] i_size - Size of buffer. + * + * @return Size of buffer extracted. + * + * If either (o_data == NULL) or (i_size == 0), rather than + * copy data from the internal buffer, the function will calculate + * the size of the data buffer needed to save all of the data + * currently in the internal buffer. + */ + uint32_t getBuffer(void * o_data, uint32_t i_dataSize) const; + + private: + + /** @brief Retrieve the trace info left behind in + * buffer from last crash + */ + void retrieveDataFromLastCrash(); + + /** The reserved trace buffer section, + * which contains the runtime traces + */ + RsvdTraceBuffer iv_rsvdTraceBuffer; + + /** errlHandl to hold the trace from the previous boot, if any */ + errlHndl_t iv_errl; + + // For testing purposes only + friend class ::RsvdTraceBuffTestSuite; + }; +} + +#endif diff --git a/src/usr/trace/runtime/test/makefile b/src/usr/trace/runtime/test/makefile index b0ad1841c..b7f134d82 100644 --- a/src/usr/trace/runtime/test/makefile +++ b/src/usr/trace/runtime/test/makefile @@ -25,6 +25,8 @@ HOSTBOOT_RUNTIME = 1 ROOTPATH = ../../../../.. +EXTRAINCDIR +=${ROOTPATH}/src/usr + MODULE = testrsvdtracebuf_rt TESTS = testrsvdtracebuf.H diff --git a/src/usr/trace/runtime/test/testrsvdtracebuf.H b/src/usr/trace/runtime/test/testrsvdtracebuf.H new file mode 100644 index 000000000..148df5d07 --- /dev/null +++ b/src/usr/trace/runtime/test/testrsvdtracebuf.H @@ -0,0 +1,1593 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/runtime/test/testrsvdtracebuf.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2013,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include <cxxtest/TestSuite.H> + +#include <trace/runtime/rt_rsvdtracebuffer.H> // TRACE::RsvdTraceBuffer +#include <trace/runtime/rt_rsvdtracebufservice.H> // TRACE::RsvdTraceBufService +#include <trace/entry.H> // TRACE::Entry +#include <trace/compdesc.H> // TRACE::ComponentDesc +#include <util/runtime/util_rt.H> // hb_get_rt_rsvd_mem + +#include <stdint.h> // uint32_t +#include <stdlib.h> // malloc +#include <string.h> // memset + + +class RsvdTraceBuffTestSuite : public CxxTest::TestSuite +{ +public: + +// Let's start with some simple tests - the constructor +void testRsvdTraceBuffConstructor() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + + if (nullptr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: beginning buffer boundary is not null", + __FILE__, __func__); + } + + if (nullptr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: ending buffer boundary is not null",__FILE__, __func__); + } + + if (nullptr != l_rsvd.iv_ptrToHead) + { + TS_FAIL("%s:%s: list head is not null", __FILE__, __func__); + } + + if (nullptr != l_rsvd.getListHead()) + { + TS_FAIL("%s:%s: list head is not null", __FILE__, __func__); + } + + if (false != l_rsvd.iv_isBufferValid) + { + TS_FAIL("%s:%s: buffer validity is not false", __FILE__, __func__); + } + + if (false != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer validity is not false", __FILE__, __func__); + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is NOT empty", __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // void testRsvdTraceBuffConstructor() + +// Some more simple tests - the initializer +void testRsvdTraceBuffInit() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + // Create a buffer + uint32_t l_bufferSize = 40; + char *l_buffer = reinterpret_cast<char*>(malloc(l_bufferSize)); + memset(l_buffer, 0 , l_bufferSize); + + // Get the address of buffer; just like we would get in "real life" + uintptr_t l_bufferAddr = reinterpret_cast<uintptr_t>(l_buffer); + + // Get a pointer to where the HEAD needs to reside in the buffer + uintptr_t *l_addressToHead = reinterpret_cast<uintptr_t *>(l_bufferAddr); + + // Initialize the buffer + TRACE::RsvdTraceBuffer l_rsvd; + l_rsvd.init(l_bufferSize - sizeof(uintptr_t), + l_bufferAddr + sizeof(uintptr_t), + l_addressToHead); + + char* l_bufferBegin = reinterpret_cast<char*>(l_bufferAddr + + sizeof(uintptr_t)); + char* l_bufferEnd = reinterpret_cast<char*>(l_bufferAddr + + l_bufferSize - 1); + + if (l_bufferBegin != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: beginning buffer boundary is not null", + __FILE__, __func__); + } + + if (l_bufferEnd != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: ending buffer boundary not set properly", + __FILE__, __func__); + } + + if (true != l_rsvd.iv_isBufferValid) + { + TS_FAIL("%s:%s: buffer validity is not true", __FILE__, __func__); + } + + if (true != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer validity is not true", __FILE__, __func__); + } + + if (nullptr == l_rsvd.iv_ptrToHead) + { + TS_FAIL("%s:%s: list head is Null", __FILE__, __func__); + } + + if (nullptr != l_rsvd.getListHead()) + { + TS_FAIL("%s:%s: list head is NOT NULL", __FILE__, __func__); + } + + if (l_bufferAddr != reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is not empty", __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffInit() + +// Test where buffer is too small to accommodate any Entry size +void testRsvdTraceBuffBufferToSmall() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + uint32_t l_bufferSize = ALIGN_8(sizeof(TRACE::Entry)) -1; + TRACE::RsvdTraceBuffer l_rsvd; + char *l_buffer = initializeRsvdBuffer(l_bufferSize, l_rsvd); + + if (l_rsvd.getBufferSize() != l_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", __FILE__, __func__); + } + + TRACE::Entry* l_entry = l_rsvd.insertEntry(0); + if (nullptr != l_entry) + { + TS_FAIL("%s:%s: Entry ptr is NOT null", __FILE__, __func__); + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is not empty", __FILE__, __func__); + } + + if (reinterpret_cast<uintptr_t>(l_buffer) != + reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + l_entry = l_rsvd.insertEntry(1); + if (nullptr != l_entry) + { + TS_FAIL("%s:%s: Entry ptr is NOT null", __FILE__, __func__); + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is not empty", __FILE__, __func__); + } + + if (reinterpret_cast<uintptr_t>(l_buffer) != + reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + l_entry = l_rsvd.insertEntry(sizeof(TRACE::Entry)); + if (nullptr != l_entry) + { + TS_FAIL("%s:%s: Entry ptr is NOT null", __FILE__, __func__); + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is not empty", __FILE__, __func__); + } + + if (reinterpret_cast<uintptr_t>(l_buffer) != + reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + free(l_buffer); + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffBufferToSmall() + +// Test where buffer is just the right size to fit a single Entry +void testRsvdTraceBuffOnlyAccommodateOneItem() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + uint32_t l_bufferSize = ALIGN_8(sizeof(TRACE::Entry)); + uint32_t l_entrySize = l_bufferSize - sizeof(TRACE::Entry); + char *l_buffer = initializeRsvdBuffer(l_bufferSize, + l_rsvd); + + if (l_rsvd.getBufferSize() != l_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", __FILE__, __func__); + } + + TRACE::Entry* l_entry = l_rsvd.insertEntry(l_entrySize); + char* l_entryCharPtr = reinterpret_cast<char*>(l_entry); + uint32_t l_endingPos = ALIGN_8(sizeof(TRACE::Entry) + l_entrySize) - 1; + char* l_entryEndCharPtr = l_entryCharPtr + l_endingPos; + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + } + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.convertToCharPointer + (l_rsvd. + getEndingAddressOfEntry(l_entry))) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (reinterpret_cast<uintptr_t>(l_buffer) != + reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add another item with same size + l_entry = l_rsvd.insertEntry(l_entrySize); + l_entryCharPtr = reinterpret_cast<char*>(l_entry); + l_entryEndCharPtr = l_entryCharPtr + l_endingPos; + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr IS null", __FILE__, __func__); + } + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.convertToCharPointer + (l_rsvd. + getEndingAddressOfEntry(l_entry))) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (reinterpret_cast<uintptr_t>(l_buffer) != + reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add another item with size+ + l_entry = l_rsvd.insertEntry(10); + l_entryCharPtr = reinterpret_cast<char*>(l_entry); + if (nullptr != l_entry) + { + TS_FAIL("%s:%s: Entry ptr is not NULL", __FILE__, __func__); + } + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (reinterpret_cast<uintptr_t>(l_buffer) != + reinterpret_cast<uintptr_t>(l_rsvd.iv_ptrToHead)) + { + TS_FAIL("%s:%s: Pointer to list is not correct", __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + free(l_buffer); + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffOnlyAccommodateOneItem() + +// Test where buffer is just the right size to fit a single Entry plus size +void testRsvdTraceBuffOnlyAccommodateOneItemPlusSize() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + uint32_t l_bufferSize = ALIGN_8(sizeof(TRACE::Entry) + 20); + uint32_t l_entrySize = l_bufferSize - sizeof(TRACE::Entry); + + char *l_fullBuffer = initializeRsvdBuffer(l_bufferSize, + l_rsvd); + + // Adjust buffer to the area we are interested in + char* l_buffer = l_fullBuffer + sizeof(uintptr_t); + + if (l_rsvd.getBufferSize() != l_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", __FILE__, __func__); + } + + // Add first entry - will be at beginning of buffer + TRACE::Entry* l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + char* l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + char* l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + return; // Need to quit or die + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + l_bufferSize - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add another entry - will be at beginning of buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + return; // Need to quit or die + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + l_bufferSize - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + // Add a third item - with 0 size + l_entry = l_rsvd.insertEntry(0); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = 0; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + return; // Need to quit or die + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + ALIGN_8(sizeof(TRACE::Entry)) - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + l_rsvd.clearBuffer(); + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is NOT empty", __FILE__, __func__); + } + + if (l_rsvd.iv_ptrToHead == nullptr) + { + TS_FAIL("%s:%s: pointer to head is NULL", __FILE__, __func__); + } + + if (*l_rsvd.iv_ptrToHead != NULL) + { + TS_FAIL("%s:%s: pointer to head is pointing an item",__FILE__,__func__); + } + + if (false == l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + // Add a "first" item - with 0 size + l_entry = l_rsvd.insertEntry(0); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = 0; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + return; // Need to quit or die + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + ALIGN_8(sizeof(TRACE::Entry)) - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + l_rsvd.clearBuffer(); + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is NOT empty", __FILE__, __func__); + } + + if (l_rsvd.iv_ptrToHead == nullptr) + { + TS_FAIL("%s:%s: pointer to head is NULL", __FILE__, __func__); + } + + if (*l_rsvd.iv_ptrToHead != NULL) + { + TS_FAIL("%s:%s: pointer to head is pointing an item",__FILE__,__func__); + } + + if (false == l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + // Add another entry - will be at beginning of buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + return; // Need to quit or die + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + l_bufferSize - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + free(l_fullBuffer); + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffOnlyAccommodateOneItemPlusSize() + +// Test where buffer is just the right size to fit two Entries +void testRsvdTraceBuffOnlyAccommodateTwoItems() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + uint32_t l_bufferSize = 2 * ALIGN_8(sizeof(TRACE::Entry)); + uint32_t l_entrySize = (l_bufferSize/2) - sizeof(TRACE::Entry); + + char *l_fullBuffer = initializeRsvdBuffer(l_bufferSize, + l_rsvd); + + // Adjust buffer to the area we are interested in + char* l_buffer = l_fullBuffer + sizeof(uintptr_t); + + if (l_rsvd.getBufferSize() != l_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + // Add first entry - will be at beginning of buffer + TRACE::Entry* l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + char* l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + char* l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + (l_bufferSize/2) - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add second item, should take up the rest of the space + l_entry = l_rsvd.insertEntry(l_entrySize); + TRACE::Entry *l_soonToBeHead = l_entry; + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (l_entryCharPtr != (l_buffer + (l_bufferSize/2))) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != (l_rsvd.iv_bufferBeginningBoundary + l_bufferSize/2)) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + l_bufferSize -1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add third item, should go to the front of the buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_soonToBeHead) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + l_soonToBeHead = l_entry; + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + (l_bufferSize/2) - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add fourth item, should take up the rest of the space + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_soonToBeHead) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != (l_buffer + (l_bufferSize/2))) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryCharPtr != (l_rsvd.iv_bufferBeginningBoundary + l_bufferSize/2)) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + l_bufferSize -1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + free(l_fullBuffer); + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffOnlyAccommodateTwoItems + +// Test when adding items at the ends of the buffer +void testRsvdTraceBuffTestTheEnds() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + uint32_t l_bufferSize = ALIGN_8(sizeof(TRACE::Entry) + 20); + uint32_t l_entrySize = l_bufferSize - sizeof(TRACE::Entry); + l_bufferSize *= 3; + + char *l_fullBuffer = initializeRsvdBuffer(l_bufferSize, + l_rsvd); + + // Adjust buffer to the area we are interested in + char* l_buffer = l_fullBuffer + sizeof(uintptr_t); + + if (l_rsvd.getBufferSize() != l_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + // A few needed vars + TRACE::Entry* l_entry(nullptr); + char* l_entryCharPtr(nullptr); + char* l_entryEndCharPtr(nullptr); + bool l_itemRemoved(false); + + + // Add first entry - will be at beginning of buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + l_entry->size = l_entrySize; + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + // Add second entry - will be at beginning of buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + l_entry->size = l_entrySize; + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + l_itemRemoved = l_rsvd.removeOldestEntry(); + if (true != l_itemRemoved) + { + TS_FAIL("%s:%s: unable to remove oldest item", __FILE__, __func__); + } + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + + l_entry = l_rsvd.getListHead(); + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != (l_buffer + l_entrySize + sizeof(TRACE::Entry))) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + (2 * + (l_entrySize + sizeof(TRACE::Entry))) - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add third item, should go to the end of buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (l_entryCharPtr != (l_buffer + (2 * + (l_entrySize + sizeof(TRACE::Entry))))) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != l_rsvd.iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // Add fourth item, should go to the beginning of buffer + l_entry = l_rsvd.insertEntry(l_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + l_entry->size = l_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (l_entryCharPtr != l_rsvd.iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + l_entrySize + sizeof(TRACE::Entry) -1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + free(l_fullBuffer); + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffTestTheEnds + +// Test reentrant of buffer +// Create a buffer and an item +void testRsvdTraceBuffTestReentrant () +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + iv_bufferSize = ALIGN_8(sizeof(TRACE::Entry) + 20); + iv_entrySize = iv_bufferSize - sizeof(TRACE::Entry); + iv_bufferSize *= 3; + + iv_buffer = initializeRsvdBuffer(iv_bufferSize, + l_rsvd); + + iv_bufferBeginningBoundary = l_rsvd.iv_bufferBeginningBoundary; + iv_bufferEndingBoundary = l_rsvd.iv_bufferEndingBoundary; + + // Adjust buffer to the area we are interested in + char* l_buffer = iv_buffer + sizeof(uintptr_t); + + if (l_rsvd.getBufferSize() != iv_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + if (true != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + // A few needed vars + TRACE::Entry* l_entry(nullptr); + char* l_entryCharPtr(nullptr); + char* l_entryEndCharPtr(nullptr); + + // Add first entry - will be at beginning of buffer + l_entry = l_rsvd.insertEntry(iv_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + l_entry->size = iv_entrySize; + l_entryCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getAddressOfPtr(l_entry)); + l_entryEndCharPtr = + l_rsvd.convertToCharPointer(l_rsvd.getEndingAddressOfEntry(l_entry)); + memset(l_entry->data, 'A', l_entry->size); + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (reinterpret_cast<TRACE::Entry*>(*l_rsvd.iv_ptrToHead) != l_entry) + { + TS_FAIL("%s:%s: head pointer is not pointing to first Entry", + __FILE__, __func__); + } + + if (l_entryCharPtr != l_buffer) + { + TS_FAIL("%s:%s: entry starting position is not correct", + __FILE__, __func__); + } + + if (l_entryEndCharPtr != (l_buffer + iv_entrySize + + sizeof(TRACE::Entry) - 1)) + { + TS_FAIL("%s:%s: entry ending position is not correct", + __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffTestReentrant + +// Test reentrant of buffer +// See if we can get a a hold of the buffer and check item previously added +// add another item +void testRsvdTraceBuffTestReentrant2() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + initializeRsvdBufferForReentrant(l_rsvd); + + if (l_rsvd.getBufferSize() != iv_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferEndingBoundary != iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferBeginningBoundary != iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (true != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + if (true != runSelfDiagnostics(l_rsvd)) + { + TS_FAIL("%s:%s: self diagnostics discovered an error", + __FILE__, __func__); + } + + // A few needed vars + TRACE::Entry* l_entry(nullptr); + + // Get first entry - will be at beginning of buffer + l_entry = l_rsvd.getListHead(); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + if ('A' != l_entry->data[0]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + if ('A' != l_entry->data[l_entry->size - 1]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + // Add second entry - will be at beginning of buffer + l_entry = l_rsvd.insertEntry(iv_entrySize); + l_entry->size = iv_entrySize; + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + memset(l_entry->data, 'B', l_entry->size); + if ('B' != l_entry->data[0]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + if ('B' != l_entry->data[iv_entrySize - 1]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffTestReentrant2 + +// Test reentrant of buffer +// See if we can get a a hold of the buffer and verify items added; +// remove all items +void testRsvdTraceBuffTestReentrant3() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + initializeRsvdBufferForReentrant(l_rsvd); + + if (l_rsvd.getBufferSize() != iv_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferEndingBoundary != iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferBeginningBoundary != iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (true != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + // A few needed vars + TRACE::Entry* l_entry(nullptr); + + // Get first entry + l_entry = l_rsvd.getListHead(); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + if ('A' != l_entry->data[0]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + if ('A' != l_entry->data[l_entry->size - 1]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + // Remove first + l_rsvd.removeOldestEntry(); + + // Get first entry + l_entry = l_rsvd.getListHead(); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + if ('B' != l_entry->data[0]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + if ('B' != l_entry->data[l_entry->size - 1]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + l_rsvd.clearBuffer(); + l_entry = l_rsvd.getListHead(); + if (nullptr != l_entry) + { + TS_FAIL("%s:%s: Entry ptr is NOT null", __FILE__, __func__); + return; // Need to quit or die + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is NOT empty", __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffTestReentrant3 + +// Test reentrant of buffer +// See if we can get a a hold of the buffer and add an item +void testRsvdTraceBuffTestReentrant4() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + initializeRsvdBufferForReentrant(l_rsvd); + + if (l_rsvd.getBufferSize() != iv_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferEndingBoundary != iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferBeginningBoundary != iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (true != l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is NOT empty", __FILE__, __func__); + } + + if (true != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + TRACE::Entry* l_entry(nullptr); + l_entry = l_rsvd.getListHead(); + if (nullptr != l_entry) + { + TS_FAIL("%s:%s: Entry ptr is NOT null", __FILE__, __func__); + return; // Need to quit or die + } + + // Add an entry + l_entry = l_rsvd.insertEntry(iv_entrySize); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + l_entry->size = iv_entrySize; + memset(l_entry->data, 'C', l_entry->size); + if ('C' != l_entry->data[0]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + if ('C' != l_entry->data[l_entry->size - 1]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffTestReentrant4 + + +// Test reentrant of buffer +// Finally see if we can get item +void testRsvdTraceBuffTestReentrant5() +{ + TRACFCOMP(g_trac_test, ENTER_MRK"RsvdTraceBuffTestSuite:%s", __func__); + + TRACE::RsvdTraceBuffer l_rsvd; + initializeRsvdBufferForReentrant(l_rsvd); + + if (l_rsvd.getBufferSize() != iv_bufferSize) + { + TS_FAIL("%s:%s: buffer size is not correct", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferEndingBoundary != iv_bufferEndingBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (l_rsvd.iv_bufferBeginningBoundary != iv_bufferBeginningBoundary) + { + TS_FAIL("%s:%s: buffer not properly initialized", + __FILE__, __func__); + } + + if (true == l_rsvd.isListEmpty()) + { + TS_FAIL("%s:%s: list is empty", __FILE__, __func__); + } + + if (true != l_rsvd.isBufferValid()) + { + TS_FAIL("%s:%s: buffer is not valid", __FILE__, __func__); + } + + TRACE::Entry* l_entry(nullptr); + l_entry = l_rsvd.getListHead(); + if (nullptr == l_entry) + { + TS_FAIL("%s:%s: Entry ptr is null", __FILE__, __func__); + return; // Need to quit or die + } + + if ('C' != l_entry->data[0]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + if ('C' != l_entry->data[l_entry->size - 1]) + { + TS_FAIL("%s:%s: Entry data is corrupt", + __FILE__, __func__); + } + + TRACFCOMP(g_trac_test, EXIT_MRK"RsvdTraceBuffTestSuite:%s", __func__); +} // end void testRsvdTraceBuffTestReentrant5 + +private: +// Helpful methods to help in testing +char* initializeRsvdBuffer(uint32_t i_bufferSize, + TRACE::RsvdTraceBuffer& i_rsvd) +{ + // Create a buffer + uint32_t l_realBufferSize = i_bufferSize + sizeof(uintptr_t); + char *l_buffer = reinterpret_cast<char*>(malloc(l_realBufferSize)); + memset(l_buffer, 0, l_realBufferSize); + + // Get the address of buffer; just like we would get in "real life" + uintptr_t l_bufferAddr = reinterpret_cast<uintptr_t>(l_buffer); + + // Get a pointer to where the HEAD needs to reside in the buffer + uintptr_t *l_addressToHead = reinterpret_cast<uintptr_t *>(l_bufferAddr); + + // Initialize the buffer + i_rsvd.init(i_bufferSize, + l_bufferAddr + sizeof(uintptr_t), + l_addressToHead); + + return l_buffer; +} + +void initializeRsvdBufferForReentrant(TRACE::RsvdTraceBuffer& i_rsvd) +{ + // Get the address of buffer; just like we would get in "real life" + uintptr_t l_bufferAddr = reinterpret_cast<uintptr_t>(iv_buffer); + + // Get a pointer to where the HEAD is + uintptr_t *l_addressToHead = reinterpret_cast<uintptr_t *>(iv_buffer); + + // Initialize the buffer + i_rsvd.init(iv_bufferSize, + l_bufferAddr + sizeof(uintptr_t), + l_addressToHead); +} + + +bool runSelfDiagnostics(TRACE::RsvdTraceBuffer& l_rsvd) +{ + bool l_everythingChecksOut = true; + uintptr_t l_bufferBeginningBoundary = l_rsvd.getAddressOfPtr + (l_rsvd.iv_bufferBeginningBoundary); + uintptr_t l_bufferEndingBoundary = l_rsvd.getAddressOfPtr + (l_rsvd.iv_bufferEndingBoundary); + + if (!l_rsvd.isListEmpty()) + { + uint32_t l_itemCount(0); + + TRACE::Entry* l_entry = l_rsvd.getListHead(); + TRACE::Entry* l_head = l_entry; + do + { + ++l_itemCount; + uintptr_t l_entryAddr = l_rsvd.getAddressOfPtr(l_entry); + uintptr_t l_entryAddrEnd = + l_rsvd.getEndingAddressOfEntry(l_entry); + + if (l_entryAddr < l_bufferBeginningBoundary) + { + TS_FAIL("Item [%d] at address (0x%X) precedes the " + "allocated memory location at address " + "(0x%X) by %d byte(s)", + l_itemCount, l_entryAddr, l_bufferBeginningBoundary, + (l_bufferBeginningBoundary - l_entryAddr)); + l_everythingChecksOut = false; + } + else if (l_entryAddr > l_bufferEndingBoundary) + { + TS_FAIL("Item [%d] at address (0x%X) is beyond the " + "ending allocated memory location at address " + "(0x%X) by %d byte(s)", + l_itemCount, l_entryAddr, l_bufferEndingBoundary, + (l_entryAddr - l_bufferEndingBoundary)); + l_everythingChecksOut = false; + } + else if (l_entryAddrEnd > l_bufferEndingBoundary) + { + TS_FAIL("Item [%d] at address (0x%X) overruns the " + "ending allocated memory location at address " + "(0x%X) by %d byte(s)", + l_itemCount, l_entryAddr, l_bufferEndingBoundary, + (l_entryAddrEnd - l_bufferEndingBoundary)); + l_everythingChecksOut = false; + } + else + { + uintptr_t l_entryNextAddr = l_rsvd. + getAddressOfPtr(l_entry->next); + if ((l_entryNextAddr > l_entryAddr) && + (l_entryAddrEnd >= l_entryNextAddr) ) + { + TS_FAIL("Item [%d] at address (0x%X) overruns the " + "next item at address (0x%X) by %d byte(s)", + l_itemCount, l_entryAddr, l_entryNextAddr, + (l_entryAddrEnd - l_entryNextAddr + 1)); + l_everythingChecksOut = false; + } + } // end if ... else + + l_entry = l_entry->next; + } + while(l_entry != l_head); + } // end if (!l_rsvd.isListEmpty()) + + return l_everythingChecksOut; +} + + char* iv_buffer; + uint32_t iv_bufferSize; + uint32_t iv_entrySize; + char *iv_bufferBeginningBoundary; //< Pointer to beginning of buffer + char *iv_bufferEndingBoundary; //< Pointer to end of buffer + TRACE::ComponentDesc *iv_compDesc; +}; |