diff options
author | Patrick Williams <iawillia@us.ibm.com> | 2012-10-31 16:01:11 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2012-12-14 10:18:59 -0600 |
commit | da3888270ff596441bf78535c26dee0a7d923145 (patch) | |
tree | 019b88ce38e87b8346e0ef659f058556e26dec42 /src/usr | |
parent | 6d7290eca2b0e753d1b954a56e2c82284dd23eb9 (diff) | |
download | talos-hostboot-da3888270ff596441bf78535c26dee0a7d923145.tar.gz talos-hostboot-da3888270ff596441bf78535c26dee0a7d923145.zip |
Lockless trace implementation
RTC: 35396
Change-Id: I96ea0d95606f04abb4dc2b0470345ca475b53912
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/2520
Tested-by: Jenkins Server
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr')
35 files changed, 3012 insertions, 2255 deletions
diff --git a/src/usr/errl/errlentry.C b/src/usr/errl/errlentry.C index 4dff81688..154eb17fd 100644 --- a/src/usr/errl/errlentry.C +++ b/src/usr/errl/errlentry.C @@ -178,9 +178,7 @@ bool ErrlEntry::collectTrace(const char i_name[], const uint64_t i_max) { // By passing nil arguments 2 and 3, obtain the size of the buffer. // Besides getting buffer size, it validates i_name. - uint64_t l_cbFull = TRACE::Trace::getTheInstance().getBuffer( i_name, - NULL, - 0 ); + uint64_t l_cbFull = TRACE::getBuffer( i_name, NULL,0 ); if( 0 == l_cbFull ) { // Problem, likely unknown trace buffer name. @@ -204,10 +202,7 @@ bool ErrlEntry::collectTrace(const char i_name[], const uint64_t i_max) l_pBuffer = new char[ l_cbBuffer ]; // Get the data into the buffer. - l_cbOutput = - TRACE::Trace::getTheInstance().getBuffer( i_name, - l_pBuffer, - l_cbBuffer ); + l_cbOutput = TRACE::getBuffer( i_name, l_pBuffer, l_cbBuffer ); if( 0 == l_cbOutput ) { diff --git a/src/usr/errl/test/errltest.H b/src/usr/errl/test/errltest.H index 2383d45f7..ccec16892 100644 --- a/src/usr/errl/test/errltest.H +++ b/src/usr/errl/test/errltest.H @@ -43,6 +43,7 @@ #include <hwas/common/hwasCallout.H> #include <hwas/common/deconfigGard.H> +#include "../../trace/entry.H" #define TEST_SEVERITY ERRORLOG::ERRL_SEV_INFORMATIONAL @@ -160,7 +161,8 @@ public: break; } - fOK = l_err->collectTrace( "TARG", sizeof(trace_buf_head_t)-2); + fOK = l_err->collectTrace("TARG", + sizeof(TRACE::trace_buf_head_t)-2); if( fOK ) { // expect an error, buffer not big enough @@ -168,7 +170,8 @@ public: break; } - fOK = l_err->collectTrace( "TARG", sizeof( trace_buf_head_t )); + fOK = l_err->collectTrace("TARG", + sizeof(TRACE::trace_buf_head_t)); if( !fOK ) { // Buffer is big enough for the header only. It is @@ -179,7 +182,8 @@ public: // sizeof( trace_buf_head_t ) + n bytes such that a single trace // entry cannot fit into. - uint64_t l_cb = sizeof(trace_buf_head_t) + (sizeof(trace_bin_entry_t)/2); + uint64_t l_cb = sizeof(TRACE::trace_buf_head_t) + + (sizeof(TRACE::trace_bin_entry_t)/2); fOK = l_err->collectTrace( "TARG", l_cb ); if( !fOK ) { diff --git a/src/usr/initservice/extinitsvc/extinitsvctasks.H b/src/usr/initservice/extinitsvc/extinitsvctasks.H index 5ff8c308c..aecc7372a 100644 --- a/src/usr/initservice/extinitsvc/extinitsvctasks.H +++ b/src/usr/initservice/extinitsvc/extinitsvctasks.H @@ -90,6 +90,20 @@ const TaskInfo g_exttaskinfolist[] = { }, /** + * @brief Trace daemon + */ + { + "libtracedaemon.so", // taskname + NULL, // no ptr to fnct + { + + START_TASK, // task type + EXT_IMAGE, // Extended Module + } + }, + + + /** * @brief FSI Device Driver */ diff --git a/src/usr/initservice/istepdispatcher/istepWorker.C b/src/usr/initservice/istepdispatcher/istepWorker.C index 3ebbe6722..61b752498 100644 --- a/src/usr/initservice/istepdispatcher/istepWorker.C +++ b/src/usr/initservice/istepdispatcher/istepWorker.C @@ -200,7 +200,7 @@ void iStepWorkerThread ( void * i_msgQ ) } // flush contTrace after each istep/substep returns - TRACE::Trace::getTheInstance().flushContBuffers(); + TRAC_FLUSH_BUFFERS(); msg_free( theMsg ); theMsg = NULL; diff --git a/src/usr/targeting/common/xmltohb/attribute_types.xml b/src/usr/targeting/common/xmltohb/attribute_types.xml index acf925c50..5ca1756e6 100644 --- a/src/usr/targeting/common/xmltohb/attribute_types.xml +++ b/src/usr/targeting/common/xmltohb/attribute_types.xml @@ -3183,10 +3183,21 @@ <default>1</default> </field> <field> + <name>traceContinuous</name> + <description> + Enable / Disable continuous trace. + 0b0: Continuous trace is disabled. + 0b1: Continuous trace is enabled. + </description> + <type>uint8_t</type> + <bits>1</bits> + <default>0</default> + </field> + <field> <name>reserved</name> <description>Reserved for future use</description> <type>uint8_t</type> - <bits>5</bits> + <bits>4</bits> <default>0</default> </field> </complexType> diff --git a/src/usr/targeting/common/xmltohb/simics_MURANO.system.xml b/src/usr/targeting/common/xmltohb/simics_MURANO.system.xml index d0c97d9ea..4ac14088e 100644 --- a/src/usr/targeting/common/xmltohb/simics_MURANO.system.xml +++ b/src/usr/targeting/common/xmltohb/simics_MURANO.system.xml @@ -119,6 +119,7 @@ <field><id>fsiSlaveInit</id><value>0</value></field> <field><id>mailboxEnabled</id><value>0</value></field> <field><id>fsiMasterInit</id><value>0</value></field> + <field><id>traceContinuous</id><value>1</value></field> <field><id>reserved</id><value>0</value></field> </default> </attribute> diff --git a/src/usr/targeting/common/xmltohb/simics_VENICE.system.xml b/src/usr/targeting/common/xmltohb/simics_VENICE.system.xml index f387a3d5f..349ad7fc1 100644 --- a/src/usr/targeting/common/xmltohb/simics_VENICE.system.xml +++ b/src/usr/targeting/common/xmltohb/simics_VENICE.system.xml @@ -95,6 +95,7 @@ <field><id>fsiSlaveInit</id><value>0</value></field> <field><id>mailboxEnabled</id><value>0</value></field> <field><id>fsiMasterInit</id><value>0</value></field> + <field><id>traceContinuous</id><value>1</value></field> <field><id>reserved</id><value>0</value></field> </default> </attribute> diff --git a/src/usr/targeting/common/xmltohb/vbu.system.xml b/src/usr/targeting/common/xmltohb/vbu.system.xml index 31635a758..58ba6c15c 100644 --- a/src/usr/targeting/common/xmltohb/vbu.system.xml +++ b/src/usr/targeting/common/xmltohb/vbu.system.xml @@ -112,6 +112,7 @@ <field><id>fsiSlaveInit</id><value>1</value></field> <field><id>mailboxEnabled</id><value>0</value></field> <field><id>fsiMasterInit</id><value>0</value></field> + <field><id>traceContinuous</id><value>1</value></field> <field><id>reserved</id><value>0</value></field> </default> </attribute> diff --git a/src/usr/targeting/xmltohb/TULETA.mrw.xml b/src/usr/targeting/xmltohb/TULETA.mrw.xml index b297cfe2c..199888096 100644 --- a/src/usr/targeting/xmltohb/TULETA.mrw.xml +++ b/src/usr/targeting/xmltohb/TULETA.mrw.xml @@ -161,6 +161,7 @@ <field><id>fsiSlaveInit</id><value>1</value></field> <field><id>mailboxEnabled</id><value>1</value></field> <field><id>fsiMasterInit</id><value>1</value></field> + <field><id>traceContinuous</id><value>0</value></field> <field><id>reserved</id><value>0</value></field> </default> </attribute> diff --git a/src/usr/targeting/xmltohb/genHwsvMrwXml.pl b/src/usr/targeting/xmltohb/genHwsvMrwXml.pl index 611477808..1d489d33b 100755 --- a/src/usr/targeting/xmltohb/genHwsvMrwXml.pl +++ b/src/usr/targeting/xmltohb/genHwsvMrwXml.pl @@ -1311,6 +1311,7 @@ sub generate_sys <field><id>fsiSlaveInit</id><value>1</value></field> <field><id>mailboxEnabled</id><value>1</value></field> <field><id>fsiMasterInit</id><value>1</value></field> + <field><id>traceContinuous</id><value>0</value></field> <field><id>reserved</id><value>0</value></field> </default> </attribute> diff --git a/src/usr/trace/buffer.C b/src/usr/trace/buffer.C new file mode 100644 index 000000000..921b5f2a4 --- /dev/null +++ b/src/usr/trace/buffer.C @@ -0,0 +1,588 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/buffer.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "buffer.H" +#include "bufferpage.H" +#include "entry.H" +#include "compdesc.H" +#include "daemonif.H" + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <util/align.H> + +namespace TRACE +{ + Buffer::Buffer(DaemonIf* i_daemon, size_t i_maxPages) : + iv_pagesAlloc(0), iv_pagesMax(i_maxPages), + iv_firstPage(NULL), iv_daemon(i_daemon) + { + iv_counters.totals = 0; + assert(i_maxPages > 0); + } + + void Buffer::_producerEnter() + { + locklessCounter value; + do + { + // Read current count. + value = iv_counters; + + // If there is a consumer (daemon) present, must wait. + while (value.consumerCount != 0) + { + futex_wait(&iv_counters.totals, value.totals); + value = iv_counters; + } + + // No consumers currently present, increment the producer count + // (to include us). + locklessCounter newValue = value; + newValue.producerCount++; + + // Attempt to atomically update the count. + if (__sync_bool_compare_and_swap(&iv_counters.totals, + value.totals, + newValue.totals) + ) + { + // Successful at atomic update, we're included in the count + // so we can exit the loop. + break; + } + + // Failed to update count, so try again. + } while(1); + } + + void Buffer::_producerExit() + { + locklessCounter value; + do + { + // Read current count. + value = iv_counters; + + // Decrement count to remove us. + locklessCounter newValue = value; + newValue.producerCount--; + + // Attempt to atomically update count. + if (!__sync_bool_compare_and_swap(&iv_counters.totals, + value.totals, + newValue.totals)) + { + // Failed, try again. + continue; + } + + // If we're the last producer and there is a consumer waiting, + // signal the consumer. + if((newValue.producerCount == 0) && (newValue.consumerCount != 0)) + { + futex_wake(&iv_counters.totals, UINT64_MAX); + } + + // If we're here, we are successful, so exit the loop. + break; + + } while(1); + } + + void Buffer::_consumerEnter() + { + locklessCounter value; + do + { + // Read current count. + value = iv_counters; + + // Set us up as a pending consumer. + locklessCounter newValue = value; + newValue.consumerCount = 1; + + // Attempt to atomically update counts. + if (!__sync_bool_compare_and_swap(&iv_counters.totals, + value.totals, + newValue.totals)) + { + // Failed, try again. + continue; + } + + // If there were producers waiting, we need to wait for them + // to clear out. + if (0 != newValue.producerCount) + { + do + { + futex_wait(&iv_counters.totals, newValue.totals); + newValue = iv_counters; + + } while(newValue.producerCount != 0); + } + + // If we're here, we are successful, so exit the loop. + break; + + } while(1); + } + + void Buffer::_consumerExit() + { + locklessCounter value; + do + { + // Read current count. + value = iv_counters; + + // Remove ourself as the consumer. + locklessCounter newValue = value; + newValue.consumerCount = 0; + + // Atomically update the count. + if (!__sync_bool_compare_and_swap(&iv_counters.totals, + value.totals, + newValue.totals)) + { + // Failed, try again. + continue; + } + + // Successful. Signal any producers that might be waiting and + // exit the loop. + futex_wake(&iv_counters.totals, UINT64_MAX); + break; + + } while(1); + } + + + Entry* Buffer::claimEntry(ComponentDesc* i_comp, size_t i_size) + { + // 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). + i_size = ALIGN_8(i_size + sizeof(Entry)); + + // If an entry is bigger than this amount, it can never be satisfied. + if (i_size > (PAGESIZE - sizeof(BufferPage))) + { + return NULL; + } + + // During the process of claiming an entry, the daemon must be + // blocked out from tampering with the pages. + _producerEnter(); + + Entry* l_entry = NULL; + + // No we begin the search for an entry. + do + { + BufferPage* first = iv_firstPage; + + // Attempt to claim from the current page first. + if (first) + { + l_entry = first->claimEntry(i_size); + if (l_entry) + { + // Found one, great! We're done. + break; + } + + // If there is a "next" page, another thread has already + // allocated a new page, so just wait for it to show up. + // (as in, some other thread is going to update iv_firstPage) + if (first->next) + { + continue; + } + } + + // Wasn't enough space, so try to allocate a new page. + uint64_t pagesAllocated = iv_pagesAlloc; + if (pagesAllocated >= iv_pagesMax) + { + // Not enough pages. Wait until someone frees one. + _producerExit(); + iv_daemon->signal(); + futex_wait(reinterpret_cast<uint64_t*>( + const_cast<BufferPage**>(&iv_firstPage)), + reinterpret_cast<uint64_t>(first)); + _producerEnter(); + // A page might be allocated now, start over. + continue; + } + + // Atomically update page count. + uint64_t newPagesAllocated = pagesAllocated + 1; + if (!__sync_bool_compare_and_swap(&iv_pagesAlloc, + pagesAllocated, + newPagesAllocated)) + { + // Someone beat us to updating the count, so start over. + // (another thread is also trying to allocate a page, let them + // do it). + continue; + } + + // Successfully updated the count so allocate the new page. + BufferPage* newPage = BufferPage::allocate(); + newPage->prev = first; + + // If there is a page already, update its next pointer to point + // back at this new page. + if (first) + { + if (!__sync_bool_compare_and_swap(&first->next, + NULL, + newPage)) + { + // Someone beat us to allocating the page, release it. + BufferPage::deallocate(newPage); + do + { + pagesAllocated = iv_pagesAlloc; + newPagesAllocated = pagesAllocated - 1; + } + while (!__sync_bool_compare_and_swap(&iv_pagesAlloc, + pagesAllocated, + newPagesAllocated)); + continue; + } + } + + // Now we have a page allocated, claim our entry first and then + // hook it up to master list. + l_entry = newPage->claimEntry(i_size); + + if (!__sync_bool_compare_and_swap(&iv_firstPage, + first, + newPage)) + { + // We got beat adding page to the master list, so release it + // and use that page. + BufferPage::deallocate(newPage); + do + { + pagesAllocated = iv_pagesAlloc; + newPagesAllocated = pagesAllocated - 1; + } + while (!__sync_bool_compare_and_swap(&iv_pagesAlloc, + pagesAllocated, + newPagesAllocated)); + + // The entry we claimed out of the "new" page is no longer + // valid since we've freed it. + l_entry = NULL; + continue; + } + + // Since we allocated a page, signal any other tasks that might be + // blocked waiting for a page to show up. + futex_wake(reinterpret_cast<uint64_t*>( + const_cast<BufferPage**>(&iv_firstPage)), + UINT64_MAX); + + // And since we allocated a page, wake up the daemon if we + // allocated the last page or if there are more than 4 pages + // and we are an infinite buffer. (4 pages is arbitrary). + static const size_t SIGNAL_AFTER_N_PAGES_ALLOCATED = 4; + if ((pagesAllocated == iv_pagesMax) || + ((pagesAllocated >= SIGNAL_AFTER_N_PAGES_ALLOCATED) && + (iv_pagesMax == UNLIMITED))) + { + iv_daemon->signal(); + } + + + } while(!l_entry); + + // Update component name and entry size. + l_entry->comp = i_comp; + l_entry->size = i_size - sizeof(Entry); + + // Leave critical section. + _producerExit(); + + return l_entry; + } + + BufferPage* Buffer::claimPages() + { + // Enter critical daemon section. + _consumerEnter(); + + // Take page(s) from buffer. + BufferPage* page = iv_firstPage; + iv_firstPage = NULL; + iv_pagesAlloc = 0; + + // Rewind to beginning of buffer. + if (page) + { + while(page->prev) { page = page->prev; } + } + + // Signal producers that might be waiting for pages to free up. + futex_wake(reinterpret_cast<uint64_t*>( + const_cast<BufferPage**>(&iv_firstPage)), + UINT64_MAX); + + // Exit critical daemon section. + _consumerExit(); + + return page; + } + + void Buffer::commitEntry(Entry* i_entry) + { + // Prevent daemon from making updates while we're in here. + _producerEnter(); + + // Read the component from the entry itself (added as part of claiming). + ComponentDesc* l_comp = i_entry->comp; + + // Lockless loop to update component linked-list. + do + { + // Update our entry's "next" pointer. + i_entry->next = l_comp->iv_first; + + // If there is an entry, update its "prev" pointer to this entry. + if (i_entry->next) + { + if (!__sync_bool_compare_and_swap(&i_entry->next->prev, + NULL, + i_entry)) + { + // Failed lockless update, try again. + continue; + } + } + // Otherwise, we need to update the component list since this is + // the first entry. + else + { + if (!__sync_bool_compare_and_swap(&l_comp->iv_last, + NULL, + i_entry)) + { + // Failed lockless update, try again. + continue; + } + } + + // Successful at our updates, break out. + break; + + } while(1); + + // We just added the newest entry, so update component list. + // + // It is possible that the committer of the preceeding entry was + // "slow", so we need to keep trying to update until the current + // compoment "first" points at the preceeding entry and then make + // it point at this entry. + // + while (!__sync_bool_compare_and_swap(&l_comp->iv_first, + i_entry->next, + i_entry)); + + + // Atomically increment component size. + __sync_add_and_fetch(&l_comp->iv_curSize, i_entry->size); + + // Mark entry as committed. + i_entry->committed = 1; + + // All done, release for daemon. + _producerExit(); + + } + + size_t Buffer::getTrace(ComponentDesc* i_comp, void* o_data, size_t i_size) + { + char* l_data = reinterpret_cast<char*>(o_data); + size_t l_size = 0; + size_t l_entries = 0; + + // If either the pointer is null or the buffer is 0, we're just trying + // to determine the size of the buffer. + bool determineSize = ((o_data == NULL) || (i_size == 0)); + + if (determineSize) + { + i_size = UINT64_MAX; + } + + trace_buf_head_t* header = NULL; + + // If we're actually extracting, add the fsp-trace buffer header. + if(!determineSize) + { + if (i_size < sizeof(trace_buf_head_t)) + return 0; + + header = reinterpret_cast<trace_buf_head_t*>(&l_data[l_size]); + memset(header, '\0', sizeof(trace_buf_head_t)); + + header->ver = TRACE_BUF_VERSION; + header->hdr_len = sizeof(trace_buf_head_t); + header->time_flg = TRACE_TIME_REAL; + header->endian_flg = 'B'; // Big Endian. + memcpy(&header->comp[0], &i_comp->iv_compName, TRAC_COMP_SIZE); + } + l_size += sizeof(trace_buf_head_t); + + // Prevent daemon from changing things while we're extracting. + _producerEnter(); + + size_t l_totalSize = l_size; + Entry* entry = i_comp->iv_first; + Entry* orig_entry = entry; + + do + { + if ((!entry) || (!entry->comp)) + { + break; + } + + // First walk the list backwards to find everything that will fit. + while(1) + { + // fsp-trace buffer entries have an extra word of size at the + // end. That is where the sizeof(uint32_t) comes from... + + if ((l_totalSize + entry->size + sizeof(uint32_t)) <= i_size) + { + l_totalSize += entry->size + sizeof(uint32_t); + + if ((entry->next) && + (entry->next->comp)) + { + entry = entry->next; + continue; + } + } + break; + } + + // If we didn't find anything that fit, leave. + if (l_totalSize == l_size) + { + break; + } + + // If we're just trying to find the size, we're done. + if(determineSize) + { + l_size = l_totalSize; + break; + } + + // Now we can actually copy all the entries... + while(1) + { + // Copy entry data. + memcpy(&l_data[l_size], &entry->data[0],entry->size); + l_size += entry->size; + + // Copy entry size. + uint32_t entry_size = entry->size + sizeof(uint32_t); + memcpy(&l_data[l_size], &entry_size, sizeof(uint32_t)); + l_size += sizeof(uint32_t); + + l_entries++; + + if (entry == orig_entry) + { + break; + } + else + { + entry = entry->prev; + } + }; + + } + while(0); + + // Unlock for daemon. + _producerExit(); + + // Update header. + if (header) + { + header->size = l_size; + header->next_free = l_size; + header->te_count = l_entries; + } + + return l_size; + } + + bool Buffer::consumerOp(Entry** i_condAddr, Entry* i_condVal, + Entry** i_condActAddr, + Entry* i_condActVal, + Entry** i_addr, Entry* i_val) + { + bool rc = true; + + // Get consumer lock. + _consumerEnter(); + + // Primative #1. + if (NULL != i_condAddr) + { + if (i_condVal == *i_condAddr) + { + if (NULL != i_condActAddr) + { + *i_condActAddr = i_condActVal; + } + } + else + { + rc = false; + } + } + + // Primative #2. + if (NULL != i_addr) + { + *i_addr = i_val; + } + + // Release consumer lock. + _consumerExit(); + + return rc; + } + +} diff --git a/src/usr/trace/buffer.H b/src/usr/trace/buffer.H new file mode 100644 index 000000000..04cb6f672 --- /dev/null +++ b/src/usr/trace/buffer.H @@ -0,0 +1,197 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/buffer.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_BUFFER_H +#define __TRACE_BUFFER_H + +/** @file buffer.H + * Declarations for the buffer class. + */ + +#include <trace/interface.H> +#include <sys/sync.h> + +namespace TRACEDAEMON { class Daemon; } // Forward declaration. + +namespace TRACE +{ + // Forward declarations. + class BufferPage; + class Entry; + class DaemonIf; + + /** @class Buffer + * @brief Class to manage the front-side (client) buffers + * + * Provides interfaces for getting an entry to write trace data to and + * committing that entry as well as extracting the component trace. + * + * Private interfaces for the daemon to ensure lockless ordering is + * maintained. + */ + class Buffer + { + public: + + enum + { + /** Used as a parameter to the constructor to indicate that + * the buffer should never block (and could instead run + * the system out of memory). */ + UNLIMITED = UINT32_MAX + }; + + /** Constructor. + * + * @param[in] i_daemon - Daemon interface for this buffer. + * @param[in] i_maxPages - Maximum number of pages to consume + * before 'claimEntry' blocks. + */ + Buffer(DaemonIf* i_daemon, size_t i_maxPages = 4); + + /** @brief Claim an entry from the buffer to write data to. + * + * @param[in] i_comp - Component which will own entry. + * @param[in] i_size - Size of data portion of entry. + * + * @return An entry to write to. + * + * If pages allocated is at max pages and there is no room to + * create an entry, this function may block. For components + * which cannot block, it is expected that the buffer will be + * constructed large enough that we run out of memory before + * we block. + */ + Entry* claimEntry(ComponentDesc* i_comp, size_t i_size); + + /** @brief Commit entry to buffer and component linked-list. + * + * @param[in] i_entry - Entry to commit. + */ + void commitEntry(Entry* i_entry); + + /** @brief Extract component trace. + * + * See service::getBuffer. Defined here due to lockless ordering + * needs. + */ + size_t getTrace(ComponentDesc* i_comp, void* o_data, size_t i_size); + + private: + volatile uint64_t iv_pagesAlloc; //< Number of pages allocated. + uint64_t iv_pagesMax; //< Maximum pages allowed. + + BufferPage* volatile iv_firstPage; //< Most recent page. + + /** @union locklessCounter + * + * This class is mostly lockless in that multiple trace entries + * can be created at the same time along with buffer extraction. + * This means that all client operations are lockless. + * + * There are minor operations that the daemon must do to ensure + * that a client is not in the middle of another operation, such + * as when the daemon removes the pages from the front-end to + * merge them into the backend. During this time, the daemon + * will obtain the equivalent of a write-lock, which will block + * all clients. + * + * It is required that all times where the write-lock is held + * are short and cannot cause page-faults that might deadlock + * components such as VFS and PNOR. This means that all code + * inside the write-lock is in the base image. + * + * This read/write lock is modeled here as "producers" (clients) + * and a "consumer" (daemon). The active counts of each are + * recorded with this structure and unioned to a single uint64_t + * so that they can be atomically updated as one entity. + */ + union locklessCounter + { + struct + { + uint32_t producerCount; + uint32_t consumerCount; + }; + uint64_t totals; + }; + + locklessCounter iv_counters; //< The producer/consumer counts. + + DaemonIf* iv_daemon; //< Daemon interface. + + /** @brief Claim front-side buffer pages to be merged into the + * common buffer. + * + * @return Pointer to the first page (most recent) from this + * buffer. + * + * Gives ownership of all the current pages to the caller and + * resets this buffer's allocated page counts. + */ + BufferPage* claimPages(); + + /** @brief Perform operations with the consumer lock held. + * + * These operations cannot be done in the daemon code directly + * because the daemon code is swappable and we cannot block + * producers. + * + * This function gives a set of primatives to the daemon to + * manipulate Entry pointers while secured under the consumer lock. + * + * The primatives that can be done are: + * 1) if (condAddr == condVal) { condActAddr = condActVal } + * 2) addr = val + * + * If any of the address parameters are NULL, their action will + * not be performed. So, just calling consumerOp() has the + * side-effect of simply toggling the consumer lock on and off. + * + * The return value indicates if primative #1 failed. + * + * @param[in] i_condAddr - The address to read for primative 1. + * @param[in] i_condVal - The value to expect for primative 1. + * @param[in] i_condActAddr - The address to store for primative 1. + * @param[in] i_condActVal - The value to store for primative 1. + * + * @param[in] i_addr - The address to store for primative 2. + * @param[in] i_val - The value to store for primative 2. + * + * @return bool - false if condAddr != condVal, true otherwise. + */ + bool consumerOp(Entry** i_condAddr = NULL, Entry* i_condVal = NULL, + Entry** i_condActAddr = NULL, + Entry* i_condActVal = NULL, + Entry** i_addr = NULL, Entry* i_val = NULL); + + void _producerEnter(); //< Enter client section. + void _producerExit(); //< Exit client section. + void _consumerEnter(); //< Enter daemon section. + void _consumerExit(); //< Exit daemon section. + + friend class BufferTest; + friend class TRACEDAEMON::Daemon; + }; +} + +#endif diff --git a/src/usr/trace/bufferpage.C b/src/usr/trace/bufferpage.C new file mode 100644 index 000000000..b4e655980 --- /dev/null +++ b/src/usr/trace/bufferpage.C @@ -0,0 +1,79 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/bufferpage.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include "bufferpage.H" + +#include <limits.h> +#include <stdlib.h> +#include <kernel/pagemgr.H> + +namespace TRACE +{ + + Entry* BufferPage::claimEntry(size_t i_size) + { + uint64_t l_usedSize = this->usedSize; + + // Verify there is enough space. + while ((usedSize + i_size) < (PAGESIZE - sizeof(BufferPage))) + { + // Atomically attempt to claim i_size worth. + uint64_t newSize = l_usedSize + i_size; + if (!__sync_bool_compare_and_swap(&this->usedSize, + l_usedSize, + newSize)) + { + // Failed race to atomically update. + // Re-read size, try again. + l_usedSize = this->usedSize; + continue; + } + + // Successful at claiming an entry, return pointer to it. + return reinterpret_cast<Entry*>(&this->data[0] + l_usedSize); + } + + return NULL; + } + + BufferPage* BufferPage::allocate(bool i_common) + { + BufferPage* page = NULL; + + page = reinterpret_cast<BufferPage*>(PageManager::allocatePage()); + memset(page, '\0', PAGESIZE); + + if (i_common) + { + page->commonPage = 1; + } + + return page; + } + + void BufferPage::deallocate(BufferPage* i_page) + { + PageManager::freePage(i_page); + } + +} diff --git a/src/usr/trace/bufferpage.H b/src/usr/trace/bufferpage.H new file mode 100644 index 000000000..7493fc512 --- /dev/null +++ b/src/usr/trace/bufferpage.H @@ -0,0 +1,73 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/bufferpage.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_BUFFERPAGE_H +#define __TRACE_BUFFERPAGE_H + +#include <trace/interface.H> + +namespace TRACE +{ + class Entry; + + /** @struct BufferPage + * + * A structure which can be overlaid onto a page of memory to allocate + * entries within the page. + */ + struct BufferPage + { + BufferPage* next; //< Linked-list 'next' ptr. + BufferPage* prev; //< Linked-list 'prev' ptr. + + uint32_t commonPage; //< Status used by daemon pages. + uint32_t usedSize; //< Size already allocated for entries. + + char data[0]; //< Data for entries. + + /** @brief Lockless-ly claim an entry from the page. + * + * @param[in] i_size - Size in bytes. + * + * @return Entry* or NULL, if not enough space. + */ + Entry* claimEntry(size_t i_size); + + /** @brief Utility function to allocate a page. + * + * @param[in] i_common - Page is "common" from a daemon perspective. + * + * @return Pointer to page. + */ + static BufferPage* allocate(bool i_common = false); + + /** @brief Utility function to free a page. + * + * @param[in] i_page - Page to deallocate. + */ + static void deallocate(BufferPage* i_page); + + }; +} + +#endif + diff --git a/src/usr/trace/compdesc.C b/src/usr/trace/compdesc.C new file mode 100644 index 000000000..92b306949 --- /dev/null +++ b/src/usr/trace/compdesc.C @@ -0,0 +1,98 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/compdesc.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "compdesc.H" +#include "service.H" +#include <assert.h> +#include <string.h> +#include <string_ext.h> + +namespace TRACE +{ + ComponentDesc::ComponentDesc(const char* i_comp, uint32_t i_size, + uint8_t i_bufferType) : + iv_bufferType(i_bufferType), iv_debugEnabled(false), + iv_maxSize(i_size), iv_curSize(0), + iv_first(NULL), iv_last(NULL) + { + assert(iv_bufferType < BUFFER_COUNT); + + memset(iv_compName, '\0', sizeof(iv_compName)); + strcpy(iv_compName, i_comp); // No buffer overrun because of assert + // in ComponentList::getDescriptor. + strupr(iv_compName); + + iv_compNameLen = strlen(iv_compName) + 1; + } + + ComponentList::ComponentList() : + iv_components() + { + mutex_init(&iv_mutex); + }; + + ComponentList::~ComponentList() + { + mutex_destroy(&iv_mutex); + }; + + ComponentDesc* ComponentList::getDescriptor(const char* i_comp, + uint32_t i_size, + uint8_t i_bufferType) + { + ComponentDesc* l_rc = NULL; + + assert(strlen(i_comp) < ComponentDesc::COMP_SIZE); + + // Fix up the component name to be upper case. + char l_compName[ComponentDesc::COMP_SIZE]; + memset(l_compName, '\0', sizeof(l_compName)); + strcpy(l_compName, i_comp); + strupr(l_compName); + + mutex_lock(&iv_mutex); + + // Look for existing descriptor. + for(List::iterator i = iv_components.begin(); + i != iv_components.end(); + ++i) + { + if (0 == memcmp(&i->iv_compName, l_compName, sizeof(l_compName))) + { + l_rc = &(*i); + break; + } + } + + // Insert new descriptor if none found. + if ((NULL == l_rc) && (i_size > 0)) + { + iv_components.push_back(ComponentDesc(l_compName, i_size, + i_bufferType)); + l_rc = &iv_components.back(); + } + + mutex_unlock(&iv_mutex); + return l_rc; + } + +}; diff --git a/src/usr/trace/compdesc.H b/src/usr/trace/compdesc.H new file mode 100644 index 000000000..48270add0 --- /dev/null +++ b/src/usr/trace/compdesc.H @@ -0,0 +1,146 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/compdesc.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_COMPDESC_H +#define __TRACE_COMPDESC_H + +/** @file compdesc.H + * + * @brief Defines the classes for component descriptors and a singleton list + * of descriptors. + */ + +#include <trace/interface.H> +#include <sys/sync.h> +#include <list> + +namespace TRACEDAEMON { class Daemon; } // Forward declaration. + +namespace TRACE +{ + + class Entry; // Forward declaration. + + /** @class ComponentDesc + * + * @brief The descriptor for a particular component. + * + * @note These should not be created directly, but by the ComponentList + * interfaces. + */ + class ComponentDesc + { + public: + friend class ComponentList; + friend class Service; + friend class Buffer; + friend class TRACEDAEMON::Daemon; + + private: + /** Constructor + * + * @param[in] i_comp - Component name. + * @param[in] i_size - Size limit of buffer. + * @param[in] i_bufferType - Type of buffer this component is + * assigned to. + */ + ComponentDesc(const char* i_comp, uint32_t i_size, + uint8_t i_bufferType = BUFFER_FAST); + + /** Max ength of component names. */ + enum { COMP_SIZE = TRAC_COMP_SIZE }; + + char iv_compName[COMP_SIZE]; //< Component name. + uint8_t iv_compNameLen; //< Length in bytes of name. + + uint8_t iv_bufferType; //< Buffer type. + bool iv_debugEnabled; //< Debug or Field-only traces. + + uint32_t iv_maxSize; //< Size limit. + uint32_t iv_curSize; //< Current size. + + Entry* iv_first; //< First (newest) trace entry. + Entry* iv_last; //< Last (oldest) trace entry. + }; + + /** @class ComponentList + * + * @brief Maintains the global list of component trace descriptors. + * + * It is expected that a singleton instance of this exists and all + * trace descriptors are allocated from it. + */ + class ComponentList + { + public: + typedef std::list<ComponentDesc> List; + + /** Default constructor */ + ComponentList(); + + /** Default destructor */ + ~ComponentList(); + + /** Get descriptor for a component + * + * @param[in] i_comp - Name of the component. + * @param[in] i_size - Size limit of buffer. + * @param[in] i_bufferType - Buffer type. + * + * If i_size is 0, that implies this is not "creating" a buffer + * but "requesting" the buffer, for instance to pass to the + * buffer-extract functions. In that case, if the buffer does + * not currently exist we don't know what size-limit to use + * when creating the buffer, so the function will return NULL. + * + * @return Descriptor or NULL. + */ + ComponentDesc* getDescriptor(const char* i_comp, uint32_t i_size, + uint8_t i_bufferType = BUFFER_FAST); + + /* Thread-safe iterator functions (for daemon). */ + /** Get the first component descriptor + * + * @param[out] o_comp - Iterator to first component. + * + * @return bool - True if empty-list. + */ + bool first(List::iterator& o_comp); + /** Get the next component descriptor + * + * @param[in,out] io_comp - Iterator to increment. + * + * @return bool - True if iterator was the last descriptor. + * + * Ex. "if (io_comp->next == iv_components.end()) return true" + */ + bool next(List::iterator& io_comp); + + private: + /** List of component descriptors. */ + List iv_components; + /** Mutex for thread-safety. */ + mutex_t iv_mutex; + }; +} + +#endif diff --git a/src/usr/trace/daemon/complist.C b/src/usr/trace/daemon/complist.C new file mode 100644 index 000000000..95f2128d3 --- /dev/null +++ b/src/usr/trace/daemon/complist.C @@ -0,0 +1,51 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/daemon/complist.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "../compdesc.H" + +namespace TRACE +{ + bool ComponentList::first(ComponentList::List::iterator& o_comp) + { + bool l_rc = true; + + mutex_lock(&iv_mutex); + o_comp = iv_components.begin(); + l_rc = (o_comp != iv_components.end()); + mutex_unlock(&iv_mutex); + + return l_rc; + } + + bool ComponentList::next(ComponentList::List::iterator& io_comp) + { + bool l_rc = true; + + mutex_lock(&iv_mutex); + io_comp++; + l_rc = (io_comp != iv_components.end()); + mutex_unlock(&iv_mutex); + + return l_rc; + } + +} diff --git a/src/usr/trace/daemon/daemon.C b/src/usr/trace/daemon/daemon.C new file mode 100644 index 000000000..2a98f70b5 --- /dev/null +++ b/src/usr/trace/daemon/daemon.C @@ -0,0 +1,578 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/daemon/daemon.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "daemon.H" +#include "../daemonif.H" +#include "../service.H" +#include "../buffer.H" +#include "../bufferpage.H" +#include "../entry.H" +#include "../compdesc.H" + +#include <initservice/taskargs.H> +#include <initservice/initserviceif.H> + +#include <sys/msg.h> +#include <kernel/console.H> +#include <util/align.H> + +#include <targeting/common/commontargeting.H> +#include <devicefw/userif.H> + +namespace TRACE +{ + // Functions from DaemonIf that are only used by the daemon itself, + // so implemented here to decrease size of base module. + + void DaemonIf::start() + { + iv_running = true; + } + + void DaemonIf::clearSignal() + { + // Atomically reset signal. + __sync_lock_test_and_set(&iv_signalled, 0); + } +} + +namespace TRACEDAEMON +{ + using namespace TRACE; + + Daemon::Daemon() : iv_service(NULL), iv_totalPruned(0) + { + iv_first = iv_last = BufferPage::allocate(true); + } + + Daemon::~Daemon() + { + assert(0); + } + + void Daemon::execute() + { + // Mark the daemon as started in the interface. + iv_service = Service::getGlobalInstance(); + iv_service->iv_daemon->start(); + + // Register shutdown events with init service. + // Do one at the "beginning" and "end" of shutdown processesing. + // The one at the beginning will flush out everything prior to + // the shutdown sequencing and get it out the mailbox. + // + // The one at the end will only be useful in non-FSP environments + // for continuous trace, because the mailbox is already shutdown. + // + INITSERVICE::registerShutdownEvent(iv_service->iv_daemon->iv_queue, + DaemonIf::TRACE_DAEMON_SIGNAL, + INITSERVICE::HIGHEST_PRIORITY); + INITSERVICE::registerShutdownEvent(iv_service->iv_daemon->iv_queue, + DaemonIf::TRACE_DAEMON_SIGNAL, + INITSERVICE::LOWEST_PRIORITY); + + // Clear scratch register. + writeScratchReg(0); + + // Loop handling messages. + while (msg_t* msg = iv_service->iv_daemon->wait()) + { + switch (msg->type) + { + // Signal from client. + case DaemonIf::TRACE_DAEMON_SIGNAL: + { + // Collect trace entries from client. + collectTracePages(); + + // Reduce buffer space in daemon-side buffer. + pruneTraceEntries(); + coalescePages(); + + break; + } + + // Shutdown message. + case DaemonIf::TRACE_DAEMON_SHUTDOWN: + { + // The main daemon should never shut down. + assert(0); + break; + } + + + default: + { + assert(0); + break; + } + } + + // Delete or respond as appropriate. + if (msg_is_async(msg)) + { + msg_free(msg); + } + else + { + msg_respond(iv_service->iv_daemon->iv_queue, msg); + } + } + } + + void* Daemon::start(void* unused) + { + Singleton<Daemon>::instance().execute(); + return NULL; + } + + void daemonProcess(errlHndl_t& o_errl) + { + task_create(&Daemon::start, NULL); + } + + + void Daemon::collectTracePages() + { + // Clear indication from clients. + iv_service->iv_daemon->clearSignal(); + + // Collect buffer pages from front-end. + BufferPage* srcPages[BUFFER_COUNT]; + for (size_t i = 0; i < BUFFER_COUNT; i++) + { + iv_curPages[i] = srcPages[i] = + iv_service->iv_buffers[i]->claimPages(); + + iv_curOffset[i] = 0; + } + + char* contBuffer = NULL; + size_t contBufferSize = 0; + + // Process buffer pages. + do + { + size_t whichBuffer = BUFFER_COUNT; + Entry* whichEntry = NULL; + uint64_t minTimeStamp = UINT64_MAX; + + // Find the entry with the earliest timestamp. + for (size_t i = 0; i < BUFFER_COUNT; i++) + { + if (NULL == iv_curPages[i]) continue; + + Entry* entry = + reinterpret_cast<Entry*>( + &((&(iv_curPages[i]->data[0]))[iv_curOffset[i]]) + ); + + trace_bin_entry_t* binEntry = + reinterpret_cast<trace_bin_entry_t*>( + &(entry->data[0]) + ); + + // Wait for entry to be committed. + while(unlikely(entry->committed == 0)) + { + task_yield(); + } + isync(); + + uint64_t curTimeStamp = + TWO_UINT32_TO_UINT64(binEntry->stamp.tbh, + binEntry->stamp.tbl); + + if (curTimeStamp < minTimeStamp) + { + whichBuffer = i; + whichEntry = entry; + minTimeStamp = curTimeStamp; + } + } + + // Did not find another entry, our work is done. + if (whichBuffer == BUFFER_COUNT) + { + break; + } + + // Increment pointers to next buffer entry. + iv_curOffset[whichBuffer] += whichEntry->size + sizeof(Entry); + if (iv_curOffset[whichBuffer] >= iv_curPages[whichBuffer]->usedSize) + { + iv_curPages[whichBuffer] = iv_curPages[whichBuffer]->next; + iv_curOffset[whichBuffer] = 0; + } + + // Calculate the sizes of the entry. + size_t contEntryDataLength = + reinterpret_cast<trace_bin_entry_t*>(&whichEntry->data[0]) + ->head.length + sizeof(trace_bin_entry_t); + + size_t contEntrySize = whichEntry->comp->iv_compNameLen + + contEntryDataLength; + + // Allocate a new continuous trace page if needed. + if ((NULL == contBuffer) || + ((contBufferSize + contEntrySize) >= PAGESIZE)) + { + if (NULL != contBuffer) + { + sendContBuffer(contBuffer, contBufferSize); + } + + contBuffer = reinterpret_cast<char*>(malloc(PAGESIZE)); + memset(contBuffer, '\0', PAGESIZE); + contBuffer[0] = TRACE_BUF_CONT; + contBufferSize = 1; + } + + // Add entry to continous trace. + memcpy(&contBuffer[contBufferSize], + whichEntry->comp->iv_compName, + whichEntry->comp->iv_compNameLen); + contBufferSize += whichEntry->comp->iv_compNameLen; + + memcpy(&contBuffer[contBufferSize], + &whichEntry->data[0], + contEntryDataLength); + contBufferSize += contEntryDataLength; + + // Allocate a new back-end entry. + Entry* mainBuffEntry = NULL; + while (NULL == + (mainBuffEntry = + iv_last->claimEntry(whichEntry->size + sizeof(Entry)))) + { + BufferPage* n = BufferPage::allocate(true); + + n->next = iv_last; + iv_last->prev = n; + iv_last = n; + } + + // Move entry from front-end buffer to back-end. + replaceEntry(whichEntry, mainBuffEntry); + + } while(1); + + // Send remainder of continous trace buffer. + if (NULL != contBuffer) + { + if (contBufferSize > 1) + { + sendContBuffer(contBuffer, contBufferSize); + } + else + { + free(contBuffer); + } + } + + // Release pages. + for (size_t i = 0; i < BUFFER_COUNT; i++) + { + // Toggle lock to ensure no trace extract currently going on. + iv_service->iv_buffers[i]->consumerOp(); + + while(srcPages[i]) + { + BufferPage* tmp = srcPages[i]->next; + BufferPage::deallocate(srcPages[i]); + srcPages[i] = tmp; + } + } + + } + + + void Daemon::sendContBuffer(void* i_buffer, size_t i_size) + { + // Write scratch register indicating continuous trace is available. + writeScratchReg(1ull << 32); + + // Signal for simics. + asm volatile("mr 4, %0; mr 5, %1" :: + "r" (i_buffer), "r" (i_size) : "4", "5"); + MAGIC_INSTRUCTION(MAGIC_CONTINUOUS_TRACE); + + TARGETING::Target* sys = NULL; + TARGETING::targetService().getTopLevelTarget(sys); + + TARGETING::SpFunctions spFunctions = + sys->getAttr<TARGETING::ATTR_SP_FUNCTIONS>(); + + if (!spFunctions.traceContinuous) + { + // Trace isn't enabled so just discard the buffer. + free(i_buffer); + } + else + { + if (spFunctions.mailboxEnabled) + { + // TODO: Send message to FSP. + free(i_buffer); + } + else + { + // Wait for tools to extract the buffer. + while(0 != readScratchReg()) + { + task_yield(); + } + } + } + } + + void Daemon::replaceEntry(Entry* from, Entry* to) + { + do + { + // Copy entry content to new entry. + memcpy(to, + from, + from->size + sizeof(Entry)); + + // Update next object's pointer. + if (to->next) + { + to->next->prev = to; + } + else + { + to->comp->iv_last = to; + } + lwsync(); // Ensure pointer update is globally visible + // (to order before 'prev' object updates). + + // Update prev object's pointer. + // Buffer ensures that an entries "next" is written before + // the "next->prev", so we can be certain that if to->prev + // then to->prev->next is finalized. + if (to->prev) + { + to->prev->next = to; + } + else // If there is no previous, this is the first (most recent) + // for the component, so update the component object. + { + Buffer* b = + iv_service->iv_buffers[to->comp->iv_bufferType]; + + // Need to toggle the consumer lock on this one, so use + // the consumerOp to move the compoment->first from + // 'from' to 'to'. + // + // If it fails (first != from anymore) then retry this + // whole sequence. + if (!b->consumerOp(&to->comp->iv_first, from, + &to->comp->iv_first, to)) + { + continue; + } + } + + // Successfully updated everything, break from loop. + break; + + } while (1); + } + + void Daemon::pruneTraceEntries() + { + ComponentList::List::iterator component; + + size_t pruned = 0; + + // Iterate through the components... + bool more = iv_service->iv_compList->first(component); + while(more) + { + Entry* entry = component->iv_last; + Entry* orig_entry = entry; + + // Invalidate entries until the component is small enough. + while((entry) && (component->iv_curSize > component->iv_maxSize)) + { + if (!reinterpret_cast<BufferPage*>( + ALIGN_PAGE_DOWN( + reinterpret_cast<uint64_t>(entry)))->commonPage) + { + break; + } + + entry->comp = NULL; // Invalidate entry. + + __sync_sub_and_fetch(&component->iv_curSize, entry->size); + pruned += entry->size; + + entry = entry->prev; + } + + if (entry != orig_entry) + { + printkd("%s,", component->iv_compName); + + // Break chain of linked list. + if (entry != NULL) + { + entry->next = NULL; + } + + // Update component pointers. + Buffer* b = + iv_service->iv_buffers[component->iv_bufferType]; + + // consumerOp pseudo-code: + // if (entry == NULL) component->first = NULL; + // component->last = entry; + b->consumerOp(&entry, NULL, + &component->iv_first, NULL, + &component->iv_last, entry); + } + + // Get next component. + more = iv_service->iv_compList->next(component); + } + + // Record size of pruned entries in a global. + if (pruned) + { + printkd(": pruned %ld\n", pruned); + iv_totalPruned += pruned; + } + } + + void Daemon::coalescePages() + { + // Skip if there hasn't been enough entries pruned to make this + // worth our while. + if (iv_totalPruned < PAGESIZE) { return; } + + + iv_totalPruned = 0; + + // Allocate a new back-end page for the coalesced entries. + BufferPage* outputPage = BufferPage::allocate(true); + BufferPage* originalOutputPage = outputPage; + + // Get the first page from the original back-end buffer. + BufferPage* currentPage = iv_first; + + // Iterate through the back-end pages. + while(currentPage != NULL) + { + + // Look at all the entries on the back-end pages. + size_t offset = 0; + while (offset < currentPage->usedSize) + { + Entry* entry = + reinterpret_cast<Entry*>(¤tPage->data[offset]); + + if (NULL != entry->comp) // Ensure entry is valid. + { + Entry* newEntry = NULL; + + // Allocate space on new back-end pages. + while (NULL == (newEntry = + outputPage->claimEntry(entry->size + sizeof(Entry)))) + { + BufferPage* newPage = BufferPage::allocate(true); + + newPage->next = outputPage; + outputPage->prev = newPage; + outputPage = newPage; + } + + // Move entry to new back-end page. + replaceEntry(entry, newEntry); + } + + offset += entry->size + sizeof(Entry); + } + + currentPage = currentPage->prev; + } + + BufferPage* oldPage = iv_first; + + // Update back-end buffer pointers to point to new back-end pages. + iv_last = outputPage; + iv_first = originalOutputPage; + + // Toggle client buffers to ensure no trace extract is going on + // on the old back-end pages. + for (size_t i = 0; i < BUFFER_COUNT; i++) + { + iv_service->iv_buffers[i]->consumerOp(); + } + + // Delete the old back-end pages. + while(oldPage) + { + BufferPage* temp = oldPage->prev; + BufferPage::deallocate(oldPage); + oldPage = temp; + } + + } + + void Daemon::writeScratchReg(uint64_t i_value) + { + size_t l_size = sizeof(uint64_t); + + errlHndl_t l_errl = + deviceWrite(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + &i_value, l_size, + DEVICE_SCOM_ADDRESS(MB_SCRATCH_REGISTER_1)); + + if (l_errl) + { + errlCommit(l_errl, HBTRACE_COMP_ID); + } + } + + uint64_t Daemon::readScratchReg() + { + size_t l_size = sizeof(uint64_t); + uint64_t value = 0; + + errlHndl_t l_errl = + deviceRead(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + &value, l_size, + DEVICE_SCOM_ADDRESS(MB_SCRATCH_REGISTER_1)); + + if (l_errl) + { + errlCommit(l_errl, HBTRACE_COMP_ID); + } + + return value; + } + +} + +// Define an entry point for the trace daemon module. +TASK_ENTRY_MACRO( TRACEDAEMON::daemonProcess ); diff --git a/src/usr/trace/daemon/daemon.H b/src/usr/trace/daemon/daemon.H new file mode 100644 index 000000000..e6d2f5606 --- /dev/null +++ b/src/usr/trace/daemon/daemon.H @@ -0,0 +1,121 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/daemon/daemon.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_DAEMON_DAEMON_H +#define __TRACE_DAEMON_DAEMON_H + +/** @file daemon.H + * Declarations for the daemon class. + */ + +#include <stdint.h> +#include "../service.H" + + // Forward declarations. +namespace TRACE +{ + class BufferPage; + class Entry; +} + +namespace TRACEDAEMON +{ + /** @class Daemon + * + * @brief Class to handle trace background ops and mailbox messages. + */ + class Daemon + { + public: + /** Default constructor. + * + * Initializes class. + * + */ + Daemon(); + /** Destructor. + * + * Not currently supported, asserts. + */ + ~Daemon(); + + /** @fn start + * + * Thread entry-point for daemon. Calls 'execute'. + */ + static void* start(void*); + + private: + + /** @fn execute + * + * Performs daemon work. + */ + void execute(); + + /** Gather trace pages from client-side buffers. */ + void collectTracePages(); + /** Send continuous trace buffer to SP. */ + void sendContBuffer(void* i_buffer, size_t i_size); + /** Reduce component trace buffers if exceeded max size. */ + void pruneTraceEntries(); + /** Combine trace buffer pages to remove pruned entries. */ + void coalescePages(); + + /** Locklessly move a trace entry from one location to another. */ + void replaceEntry(TRACE::Entry* from, TRACE::Entry* to); + + /** Write mailbox scratch register to a value. */ + void writeScratchReg(uint64_t i_value); + /** Read mailbox scratch register. */ + uint64_t readScratchReg(); + + /** Client-service object. */ + TRACE::Service* iv_service; + + /** First (oldest) combined-buffer page. */ + TRACE::BufferPage* iv_first; + /** Last (newested) combined-buffer page. */ + TRACE::BufferPage* iv_last; + + // These are kept as class variables rather than function variables + // so that they can be extraced by the debug tools, since we might + // be in the middle of the 'collectTracePages' function when the + // debug tools try to extract the trace. + /** Intermediate pages + * After collection from client-side, before combining. */ + TRACE::BufferPage* iv_curPages[TRACE::BUFFER_COUNT]; + /** Current offset into intermediate page. */ + size_t iv_curOffset[TRACE::BUFFER_COUNT]; + + /** Current size of trace entries pruned. */ + size_t iv_totalPruned; + + /** Address of scratch register. */ + static const uint32_t MB_SCRATCH_REGISTER_1 = 0x00050038; + + }; + + +} + +#endif diff --git a/src/usr/trace/daemon/makefile b/src/usr/trace/daemon/makefile new file mode 100644 index 000000000..325810b77 --- /dev/null +++ b/src/usr/trace/daemon/makefile @@ -0,0 +1,30 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/trace/daemon/makefile $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2011,2012 +# +# p1 +# +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code +# +# The source code for this program is not published or otherwise +# divested of its trade secrets, irrespective of what has been +# deposited with the U.S. Copyright Office. +# +# Origin: 30 +# +# IBM_PROLOG_END_TAG +ROOTPATH = ../../../.. +MODULE = tracedaemon + +OBJS = daemon.o complist.o + +#SUBDIRS = test.d + +include ${ROOTPATH}/config.mk diff --git a/src/usr/trace/daemonif.C b/src/usr/trace/daemonif.C new file mode 100644 index 000000000..c03014086 --- /dev/null +++ b/src/usr/trace/daemonif.C @@ -0,0 +1,67 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/daemonif.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "daemonif.H" + +namespace TRACE +{ + + DaemonIf::DaemonIf() : + iv_queue(msg_q_create()), iv_signalled(0), iv_running(false) + { + } + + DaemonIf::~DaemonIf() + { + if (iv_running) + { + msg_t* msg = msg_allocate(); + msg->type = TRACE_DAEMON_SHUTDOWN; + msg_sendrecv(iv_queue, msg); + msg_free(msg); + } + + msg_q_destroy(iv_queue); + } + + void DaemonIf::signal(bool i_blocking) + { + // Atomically increment the signal count. + uint16_t count = __sync_fetch_and_add(&iv_signalled, 1); + + // Send a message if this is the first, or requested to be blocking. + if ((count == 0) || (i_blocking)) + { + msg_t* msg = msg_allocate(); + msg->type = TRACE_DAEMON_SIGNAL; + if (i_blocking) + { + msg_sendrecv(iv_queue, msg); // sync message due to 'blocking'. + } + else + { + msg_send(iv_queue, msg); // async message request. + } + } + } + +} diff --git a/src/usr/trace/daemonif.H b/src/usr/trace/daemonif.H new file mode 100644 index 000000000..9e59fe58a --- /dev/null +++ b/src/usr/trace/daemonif.H @@ -0,0 +1,99 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/daemonif.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_DAEMON_H +#define __TRACE_DAEMON_H + +#include <sys/msg.h> +#include <mbox/mbox_queues.H> + + // Forward declaration of Daemon class. +namespace TRACEDAEMON { class Daemon; } + +namespace TRACE +{ + /** @class DaemonIf + * + * Defines the interface between the client (front-end) interfaces and + * the daemon. + * + * Since the daemon is in a separate extended module the client won't + * know when the daemon process is actually available. This small + * interface class is used to provide an abstraction in the base module + * for clients. + * + */ + class DaemonIf + { + public: + /** Default constructor. + * + * Initializes class and sets daemon as not-running. + */ + DaemonIf(); + /** Default destructor. + * + * Ensures the daemon is shutdown if running. + */ + ~DaemonIf(); + + /** Allows the client to signal the daemon that work is ready. + * + * @param[in] i_blocking - Indicates if this function should + * block until the daemon satisfies the + * request. + */ + void signal(bool i_blocking = false); + + friend class BufferTest; + friend class TRACEDAEMON::Daemon; + + private: + /** Queue to send messages to daemon on. */ + msg_q_t iv_queue; + /** Indication if the daemon has already been signalled. */ + uint16_t iv_signalled; + /** State of daemon process. */ + bool iv_running; + + enum MSG_TYPES + { + /** Client work is ready to be performed; flush buffers. */ + TRACE_DAEMON_SIGNAL = MBOX::FIRST_SECURE_MSG | 0, + /** Daemon should shutdown. */ + TRACE_DAEMON_SHUTDOWN = MBOX::FIRST_UNSECURE_MSG | 1, + }; + + // Since the below functions are only used by the daemon, they + // are only implemented in the daemon module. + + /** Mark the daemon as 'running'. */ + void start(); + /** Block the daemon until a message is available. */ + msg_t* wait() { return msg_wait(iv_queue); } + /** Clear the client signal. */ + void clearSignal(); + + }; +} + +#endif diff --git a/src/usr/trace/entry.H b/src/usr/trace/entry.H new file mode 100644 index 000000000..1bd47ce18 --- /dev/null +++ b/src/usr/trace/entry.H @@ -0,0 +1,127 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/entry.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_ENTRY_H +#define __TRACE_ENTRY_H + +/** @file entry.H + * + * Definition of both the Hostboot-unique "entry" structure and the + * fsp-trace common entry structures. + */ + +#include <trace/interface.H> + +namespace TRACE +{ + /** @struct Entry + * + * @brief Maintains the data for an entry in the Hostboot trace buffers. + * + * In order to allow easy traversal of component trace buffers when they + * are in a common buffer, this entry maintains a linked-list of entries + * within the same buffer. + * + * Important conventions: + * * (comp == NULL) - Entry is "expired" and no longer valid. + * * (committed == 0) - Entry is still in the process of being created + * by a client. + * + * If we wished to reduce the size of this structure in the future, to + * reduce the memory impact of tracing, we could turn the pointers here + * into a "short pointer" which is only 32bits since all Hostboot heap + * addresses are under 4GB. + */ + struct Entry + { + ComponentDesc* comp; //< Component Descriptor for this Entry. + Entry* next; //< Linked-list 'next' ptr. + Entry* prev; //< Linked-list 'prev' ptr. + uint16_t committed; //< Committed status. + uint16_t size; //< Size of data portion. + char data[0]; //< Start of 'fsp-trace' style structure. + }; + + //----- Below are structures directly from FSP for fsp-trace ------// + + const uint32_t TRACE_BUF_VERSION = 0x01; // Trace buffer version + const uint32_t TRACE_COMP_TRACE = 0x434F; // Component Field Trace - "CO" + const uint32_t TRACE_FIELDTRACE = 0x4654; // Field Trace - "FT" + const uint32_t TRACE_FIELDBIN = 0x4644; // Binary Field Trace - "FD" + const uint32_t TRACE_TIME_REAL = 0; // upper 32 = secs, + // lower 32 = microsecs + const uint32_t TRACE_BUF_CONT = 2; // Trace buffer is continuous + // trace style. + + /*! + * @brief Timestamp and thread id for each trace entry. + */ + struct trace_entry_stamp_t + { + uint32_t tbh; /*!< timestamp upper part */ + uint32_t tbl; /*!< timestamp lower part */ + uint32_t tid; /*!< process/thread id */ + }; + + /* + * @brief Structure is used by adal app. layer to fill in trace info. + */ + struct trace_entry_head_t + { + uint16_t length; /*!< size of trace entry */ + uint16_t tag; /*!< type of entry: xTRACE xDUMP, (un)packed */ + uint32_t hash; /*!< a value for the (format) string */ + uint32_t line; /*!< source file line number of trace call */ + }; + + /* + * @brief Binary first writes header and time stamp. + */ + struct trace_bin_entry_t { + trace_entry_stamp_t stamp; + trace_entry_head_t head; + char data[0]; + }; + + /* + * @brief Structure is put at beginning of all trace buffers + */ + struct trace_buf_head_t { + unsigned char ver; /*!< version of this struct (1) */ + unsigned char hdr_len; /*!< size of this struct in bytes */ + unsigned char time_flg; /*!< meaning of timestamp entry field */ + unsigned char endian_flg; /*!< flag for big ('B') or little + ('L') endian */ + char comp[TRAC_COMP_SIZE]; /*!< the buffer name as specified in + init call*/ + uint32_t size; /*!< size of buffer, including this struct*/ + uint32_t times_wrap; /*!< how often the buffer wrapped */ + uint32_t next_free; /*!< offset of the byte behind the latest + entry*/ + uint32_t te_count; /*!< Updated each time a trace is done */ + uint32_t extracted; /*!< Not currently used */ + }; + + + +} +#endif diff --git a/src/usr/trace/interface.C b/src/usr/trace/interface.C new file mode 100644 index 000000000..659179233 --- /dev/null +++ b/src/usr/trace/interface.C @@ -0,0 +1,115 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/interface.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/** @file interface.C + * @brief Implementation of the trace interfaces. + * + * Most of the functions in this file are simply redirections to the + * appropriate class/instance that handles this functionality. + */ + +#include <trace/interface.H> +#include <util/singleton.H> +#include <stdarg.h> +#include <limits.h> + +#include "compdesc.H" +#include "service.H" + +namespace TRACE +{ + TracInit::TracInit(trace_desc_t** o_td, + const char * i_comp, + const size_t i_size, + uint8_t i_bufferType) + { + initBuffer(o_td, i_comp, i_size, i_bufferType); + } + + TracInit::~TracInit() {} + + + + void initBuffer(ComponentDesc** o_td, + const char* i_comp, + size_t i_size, + uint8_t i_bufferType) + { + + if (i_size == 0) + { + i_size = KILOBYTE; + } + + (*o_td) = + Singleton<ComponentList>::instance().getDescriptor(i_comp, + i_size, + i_bufferType); + } + + void trace_adal_write_all(ComponentDesc * io_td, + const trace_hash_val i_hash, + const char * i_fmt, + const uint32_t i_line, + const int32_t i_type, ...) + { + va_list args; + va_start(args, i_type); + + Singleton<Service>::instance().writeEntry(io_td, i_hash, i_fmt, + i_line, i_type, args); + + va_end(args); + } + + void trace_adal_write_bin(ComponentDesc * io_td, + const trace_hash_val i_hash, + const uint32_t i_line, + const void * i_ptr, + const uint32_t i_size, + const int32_t i_type) + { + Singleton<Service>::instance().writeBinEntry(io_td, i_hash, i_line, + i_ptr, i_size, i_type); + } + + size_t getBuffer(const char * i_comp, + void * o_data, + size_t i_bufferSize ) + { + ComponentDesc* l_comp = + Singleton<ComponentList>::instance().getDescriptor(i_comp, 0); + + if (NULL == l_comp) + { + return 0; + } + return Singleton<Service>::instance().getBuffer(l_comp, + o_data, i_bufferSize); + } + + void flushBuffers() + { + Singleton<Service>::instance().flushBuffers(); + } +}; diff --git a/src/usr/trace/makefile b/src/usr/trace/makefile index fe7cf2d92..38de606aa 100644 --- a/src/usr/trace/makefile +++ b/src/usr/trace/makefile @@ -1,30 +1,31 @@ -# IBM_PROLOG_BEGIN_TAG -# This is an automatically generated prolog. +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. # -# $Source: src/usr/trace/makefile $ +# $Source: src/usr/trace/makefile $ # -# IBM CONFIDENTIAL +# IBM CONFIDENTIAL # -# COPYRIGHT International Business Machines Corp. 2011 +# COPYRIGHT International Business Machines Corp. 2011,2012 # -# p1 +# p1 # -# Object Code Only (OCO) source materials -# Licensed Internal Code Source Materials -# IBM HostBoot Licensed Internal Code +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code # -# The source code for this program is not published or other- -# wise divested of its trade secrets, irrespective of what has -# been deposited with the U.S. Copyright Office. +# The source code for this program is not published or otherwise +# divested of its trade secrets, irrespective of what has been +# deposited with the U.S. Copyright Office. # -# Origin: 30 +# Origin: 30 # -# IBM_PROLOG_END +# IBM_PROLOG_END_TAG ROOTPATH = ../../.. MODULE = trace -OBJS = trace.o tracedaemon.o assert.o +OBJS = interface.o service.o compdesc.o buffer.o bufferpage.o daemonif.o \ + assert.o -SUBDIRS = test.d +SUBDIRS = daemon.d test.d include ${ROOTPATH}/config.mk diff --git a/src/usr/trace/service.C b/src/usr/trace/service.C new file mode 100644 index 000000000..6c52efb18 --- /dev/null +++ b/src/usr/trace/service.C @@ -0,0 +1,271 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/service.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "service.H" +#include "buffer.H" +#include "entry.H" +#include "compdesc.H" +#include "daemonif.H" + +#include <sys/time.h> +#include <sys/task.h> +#include <util/align.H> +#include <string.h> +#include <util/singleton.H> +#include <assert.h> +#include <time.h> + +namespace TRACE +{ + Service::Service() + { + iv_daemon = new DaemonIf(); + iv_buffers[BUFFER_SLOW] = new Buffer(iv_daemon, Buffer::UNLIMITED); + iv_buffers[BUFFER_FAST] = new Buffer(iv_daemon); + iv_compList = &(Singleton<ComponentList>::instance()); + } + + Service::~Service() + { + // No need to destruct the service. + assert(0); + } + + void Service::writeEntry(ComponentDesc* i_td, + trace_hash_val i_hash, + const char * i_fmt, + uint32_t i_line, + int32_t i_type, + va_list i_args) + { + do + { + // Get the right buffer for this component. + Buffer* l_buffer = iv_buffers[i_td->iv_bufferType]; + + // Copy the current time. + trace_entry_stamp_t l_time; + _createTimeStamp(&l_time); + + // Sizes for trace entry. + uint32_t l_size = 0; + uint32_t l_num_args = 0; + uint32_t l_num_words = 0; + + // Maps of arguments to special types. + uint64_t l_str_map = 0, l_char_map = 0, l_double_map = 0; + + va_list l_args; + va_copy(l_args, i_args); + + // Parse the fmt list to determine the types/sizes of each + // argument. + while(NULL != (i_fmt = strchr(i_fmt, '%'))) + { + i_fmt++; + + switch (*i_fmt) + { + case '%': + break; + + case 's': // string. + l_str_map |= (1 << l_num_args); + l_num_args++; + l_num_words++; + l_size += ALIGN_4(strlen(va_arg(l_args, char*)) + 1); + break; + + case 'c': // character. + l_char_map |= (1 << l_num_args); + l_num_args++; + l_num_words++; + va_arg(l_args, uint32_t); + l_size += sizeof(uint32_t); + break; + + case 'e': // doubles. + case 'f': + case 'g': + l_double_map |= (1 << l_num_args); + l_num_args++; + l_num_words += 2; + va_arg(l_args, double); + l_size += sizeof(double); + break; + + default: // uint64_t-sized argument. + l_num_args++; + l_num_words += 2; + va_arg(l_args, uint64_t); + l_size += sizeof(uint64_t); + break; + } + i_fmt++; + } + + va_end(l_args); + + // Ensure we don't have too many arguments. + if (l_num_args > TRAC_MAX_ARGS) + { + // Simply reducing the number of arguments and continuing + // causes FSP trace to crash. See defect 864438. + //l_num_args = TRAC_MAX_ARGS; + break; + } + + // Claim an entry from the buffer. + uint32_t l_realSize = ALIGN_4(sizeof(trace_bin_entry_t) + l_size); + Entry* l_entry = l_buffer->claimEntry(i_td, l_realSize); + if (NULL == l_entry) + { + break; + } + + // Copy contents into the 'fsp-trace'-style structure. + trace_bin_entry_t* l_binEntry = + reinterpret_cast<trace_bin_entry_t*>(&l_entry->data[0]); + + l_binEntry->stamp = l_time; + l_binEntry->head.length = l_size; + l_binEntry->head.tag = TRACE_COMP_TRACE; + l_binEntry->head.hash = i_hash; + l_binEntry->head.line = i_line; + + + // Copy arguments into the 'fsp-trace' entry's data section. + char* l_dataOut = reinterpret_cast<char*>(&l_binEntry->data[0]); + for (size_t i = 0; i < l_num_args; i++) + { + // String. + if (l_str_map & (1 << i)) + { + char* str = va_arg(i_args, char*); + uint32_t strSize = strlen(str); + + memcpy(l_dataOut, str, strSize+1); + l_dataOut += ALIGN_4(strSize + 1); + } + // Single character. + else if (l_char_map & (1 << i)) + { + *(reinterpret_cast<uint32_t*>(l_dataOut)) = + va_arg(i_args, uint32_t); + l_dataOut += sizeof(uint32_t); + } + // Doubles. + else if (l_double_map & (1 << i)) + { + *(reinterpret_cast<double*>(l_dataOut)) = + va_arg(i_args, double); + l_dataOut += sizeof(double); + } + // All others (as uint64_t's). + else + { + *(reinterpret_cast<uint64_t*>(l_dataOut)) = + va_arg(i_args, uint64_t); + l_dataOut += sizeof(uint64_t); + } + } + + // "Commit" entry to buffer. + l_buffer->commitEntry(l_entry); + + } while(0); + } + + void Service::writeBinEntry(ComponentDesc* i_td, + trace_hash_val i_hash, + uint32_t i_line, + const void* i_ptr, + uint32_t i_size, + int32_t i_type) + { + do + { + // Get the right buffer for this component. + Buffer* l_buffer = iv_buffers[i_td->iv_bufferType]; + + // Copy the current time. + trace_entry_stamp_t l_time; + _createTimeStamp(&l_time); + + // Claim an entry from the buffer. + uint32_t l_realSize = ALIGN_4(sizeof(trace_bin_entry_t) + i_size); + Entry* l_entry = l_buffer->claimEntry(i_td, l_realSize); + if (NULL == l_entry) + { + break; + } + + // Copy contents into the 'fsp-trace'-style structure. + trace_bin_entry_t* l_binEntry = + reinterpret_cast<trace_bin_entry_t*>(&l_entry->data[0]); + + l_binEntry->stamp = l_time; + l_binEntry->head.length = i_size; + l_binEntry->head.tag = TRACE_FIELDBIN; + l_binEntry->head.hash = i_hash; + l_binEntry->head.line = i_line; + + // Copy bin-data into the 'fsp-trace' entry's data section. + memcpy(&l_binEntry->data[0], i_ptr, i_size); + + // "Commit" entry to buffer. + l_buffer->commitEntry(l_entry); + + } while(0); + } + + size_t Service::getBuffer(ComponentDesc* i_comp, + void * o_data, + size_t i_size) + { + return + iv_buffers[i_comp->iv_bufferType]->getTrace(i_comp,o_data,i_size); + } + + void Service::flushBuffers() + { + iv_daemon->signal(true); + } + + Service* Service::getGlobalInstance() + { + return &(Singleton<Service>::instance()); + } + + void Service::_createTimeStamp(trace_entry_stamp_t *o_entry) + { + timespec_t curTime; + + clock_gettime(CLOCK_MONOTONIC, &curTime); + + o_entry->tbh = curTime.tv_sec; + o_entry->tbl = curTime.tv_nsec; + o_entry->tid = task_gettid(); + } + + +} diff --git a/src/usr/trace/service.H b/src/usr/trace/service.H new file mode 100644 index 000000000..2a5218cab --- /dev/null +++ b/src/usr/trace/service.H @@ -0,0 +1,161 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/service.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __TRACE_SERVICE_H +#define __TRACE_SERVICE_H + +/** @file service.H + * + * @brief Facade class for the front-side (client) end of trace. + * + * For the lock-less trace design, clients have visibility to two entities: + * Component-Descriptors and Buffers. + * + * The Descriptors contain information such as component name, debug on/off + * state, and associated buffer type as well as a linked-list of trace + * entries associated with the component. Since the trace entries are + * maintained as a linked-list, extracting the traces is simply walking + * the linked-list and copying data into the extract buffer. + * + * The Buffers are where new entries go when they are first created. The + * buffers allow us to rate-limit entries by segregating components which + * can block and those which cannot block. (Anything related to sending + * traces down to FSP via mailbox cannot block while adding a new trace) + * + * + * As the buffers start to fill up, a daemon is periodically triggered to + * harvest the trace entries and combine them into a single common buffer + * within the daemon. As entries are harvested from the front end buffers + * they are copied into "continuous trace" buffers and sent to the FSP + * (and/or VPO and simics debug scripts). + * + * The daemon is also responsible for ensuring that components do not + * exceed their trace size limit. Since component descriptors contain + * a linked list of entries, the daemon can expire entries from the end of + * the list until the size limit is no longer exceeded for a component. + * Periodically, along with expiring trace entries, the daemon will + * coalesce back-end buffer pages to fill holes caused by expired entries. + * + */ + +#include <trace/interface.H> +#include <stdarg.h> + +namespace TRACEDAEMON { class Daemon; } // Forward declaration. + +namespace TRACE +{ + // Forward declarations. + class Buffer; + class DaemonIf; + class ComponentList; + class trace_entry_stamp_t; + + /** @class Service + * + * @brief Front-end interfaces for trace. + * + * There should be a singleton instance of this class. + */ + class Service + { + public: + /** Default constructor */ + Service(); + /** Default destructor */ + ~Service(); + + /** @brief Write a normal entry to a trace buffer. + * + * @param[in] i_td - Component Descriptor to write to. + * + * @param[in] i_hash - Hash value. + * @param[in] i_fmt - Printf-style format string. + * @param[in] i_line - Line number. + * @param[in] i_type - TRACE_DEBUG / TRACE_FIELD + * + * @param[in] i_args - Arguments corresponding to i_fmt. + */ + void writeEntry(ComponentDesc* i_td, + trace_hash_val i_hash, + const char * i_fmt, + uint32_t i_line, + int32_t i_type, + va_list i_args); + + /** @brief Write a binary entry to a trace buffer. + * + * @param[in] i_td - Component Descriptor to write to. + * + * @param[in] i_hash - Hash value. + * @param[in] i_line - Line number. + * @param[in] i_ptr - Data to write. + * @param[in] i_size - Bytes to write. + * @param[in] i_type - TRACE_DEBUG / TRACE_FIELD + */ + void writeBinEntry(ComponentDesc* i_td, + trace_hash_val i_hash, + uint32_t i_line, + const void* i_ptr, + uint32_t i_size, + int32_t i_type); + + /** @brief Extract a component's trace buffer. + * + * @param[in] i_comp - Component to extract. + * + * @param[out] o_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 + * copying into the buffer, the function will calculate the size + * of the buffer needed to save all of the data currently in the + * component's trace. + */ + size_t getBuffer(ComponentDesc* i_comp, + void * o_data, + size_t i_size); + + /** @brief Flushes the front-side buffers out to continuous trace. + */ + void flushBuffers(); + + friend class TRACEDAEMON::Daemon; + + private: + /** Front-size buffers */ + Buffer* iv_buffers[BUFFER_COUNT]; // slow / fast buffers. + /** Interface to signal daemon. */ + DaemonIf* iv_daemon; + /** List of component descriptors. */ + ComponentList* iv_compList; + + /** Get the singleton instance. */ + static Service* getGlobalInstance(); + /** Copy the current time into the timestamp. */ + void _createTimeStamp(trace_entry_stamp_t* o_entry); + }; +} + +#endif diff --git a/src/usr/trace/test/testbuffer.H b/src/usr/trace/test/testbuffer.H new file mode 100644 index 000000000..d63a24ab3 --- /dev/null +++ b/src/usr/trace/test/testbuffer.H @@ -0,0 +1,92 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/test/testbuffer.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "../buffer.H" +#include "../bufferpage.H" +#include "../compdesc.H" +#include "../entry.H" +#include "../daemonif.H" + +#include <cxxtest/TestSuite.H> +#include <limits.h> +#include <sys/task.h> +#include <sys/time.h> +#include <kernel/pagemgr.H> + +namespace TRACE +{ + +class BufferTest : public CxxTest::TestSuite +{ + public: + void testClaimEntry() + { + DaemonIf d; + Buffer b(&d, 1); + + tid_t child = task_create(testClaimEntryThread, &b); + msg_free(d.wait()); + + BufferPage* page = b.claimPages(); + PageManager::freePage(page); + + task_wait_tid(child, NULL, NULL); + + page = b.claimPages(); + if (NULL == page) + { + TS_FAIL("Not enough pages created in trace buffer.\n"); + } + PageManager::freePage(page); + + page = b.claimPages(); + if (NULL != page) + { + TS_FAIL("Too many pages created in trace buffer.\n"); + } + + } + + static void* testClaimEntryThread(void* _buffer) + { + Buffer* b = reinterpret_cast<Buffer*>(_buffer); + + ComponentList l; + ComponentDesc* t = l.getDescriptor("TEST", 2048); + + static const size_t ALLOC_SIZE = 128; + + for(size_t i = 0; i < PAGESIZE/ALLOC_SIZE; i++) + { + Entry* e = b->claimEntry(t, ALLOC_SIZE - sizeof(Entry)); + if (e->comp != t) + { + TS_FAIL("Component ID is not updated in entry."); + } + } + + return NULL; + + } +}; + +} diff --git a/src/usr/trace/test/testcomplist.H b/src/usr/trace/test/testcomplist.H new file mode 100644 index 000000000..1bee55cc8 --- /dev/null +++ b/src/usr/trace/test/testcomplist.H @@ -0,0 +1,61 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/trace/test/testcomplist.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "../compdesc.H" + +#include <cxxtest/TestSuite.H> + +using namespace TRACE; + +class ComponentListTest : public CxxTest::TestSuite +{ + public: + void testGetDescriptor() + { + ComponentList l; + + ComponentDesc* t = l.getDescriptor("TEST", 2048); + + if (NULL == t) + { + TS_FAIL("Component descriptor was NULL."); + } + + ComponentDesc* t2 = l.getDescriptor("teST", 2048); + if (t2 != t) + { + TS_FAIL("Component descriptor failed case-insensitive test."); + } + + ComponentDesc* t3 = l.getDescriptor("TEST2", 0); + if (NULL != t3) + { + TS_FAIL("Component descriptor failed non-existance test."); + } + + t3 = l.getDescriptor("TEST2", 2048); + if (t3 == t) + { + TS_FAIL("Component descriptor failed uniqueness test."); + } + } +}; diff --git a/src/usr/trace/test/tracetest.H b/src/usr/trace/test/tracetest.H deleted file mode 100644 index e574f55c5..000000000 --- a/src/usr/trace/test/tracetest.H +++ /dev/null @@ -1,306 +0,0 @@ -// IBM_PROLOG_BEGIN_TAG -// This is an automatically generated prolog. -// -// $Source: src/usr/trace/test/tracetest.H $ -// -// IBM CONFIDENTIAL -// -// COPYRIGHT International Business Machines Corp. 2011 -// -// p1 -// -// Object Code Only (OCO) source materials -// Licensed Internal Code Source Materials -// IBM HostBoot Licensed Internal Code -// -// The source code for this program is not published or other- -// wise divested of its trade secrets, irrespective of what has -// been deposited with the U.S. Copyright Office. -// -// Origin: 30 -// -// IBM_PROLOG_END -#ifndef __TEST_TRACETEST_H -#define __TEST_TRACETEST_H - -/** - * @file tracetest.H - * - * @brief All unit tests for the trace module in host boot. -*/ - -#include <cxxtest/TestSuite.H> -//#include <trace/interface.H> -#include <tracinterface.H> -#include <stdio.h> - -class TraceTest : public CxxTest::TestSuite -{ -public: - - /** - * @test Test Component Trace Interfaces - */ - void testTracComp(void) - { - trace_desc_t *g_trac_test = NULL; - TRAC_INIT_BUFFER(&g_trac_test, "EXAMPLE", 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - // Component trace tests - There is no easy way to validate these - // are working but we can at least ensure we don't cause host boot - // to crash when running them. - - for(uint32_t i=0;i<100;i++) - { - TRACFCOMP(g_trac_test, "Thread ID: %d", task_gettid()); - TRACFCOMP(g_trac_test, "Validate all number types (c,u,X,d): %c %u 0x%X %d", - 'a',i,i+1,i+2); - - TRACFCOMP(g_trac_test, "Validate pointer type (p): %p", - g_trac_test); - - TRACFCOMP(g_trac_test, "64 Bit Value Test - 0x123456789ABCDEF0: 0x%X", - 0x123456789ABCDEF0); - - // Do a debug trace - TRACDCOMP(g_trac_test,"This is a debug trace"); - - // Do an strace - TRACSCOMP(g_trac_test, "STRACE: Testing all number types (c,u,X,d,s): %c %u 0x%X %d %s", - 'b',i,i+1,i+2,"Hostboot"); - - // Test formatting - TRACFCOMP(g_trac_test, "Test width.precision formatting (u,x,X,d): %8u 0x%.06x 0x%16X %01.01d", - 0xABCD,0x1234,0x123456789ABCDEF0,0x12345678); - - TRACFCOMP(g_trac_test, "Test width.precision.length formatting (u,x,X,d): %8hu 0x%.06lx 0x%16LX %01.01lld", - 0xABCD,0x1234,0x123456789ABCDEF0,0x12345678); - - TRACFCOMP(g_trac_test, "Test width.precision.length formatting (u,x,X,i): %8hu 0x%.06lx 0x%16LX %01.01lli", - 0xABCD,0x1234,0x123456789ABCDEF0,0x12345678AB); - } - - // Be sure a NULL trace descriptor does not cause a failure - TRACFCOMP(NULL,"This trace should never show up"); - - } - } - - /** - * @test Test Binary Trace Interface - */ - void testTracBinary(void) - { - trace_desc_t *g_trac_test = NULL; - TRAC_INIT_BUFFER(&g_trac_test, "EXAMPLEBIN", 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - // Binary trace tests - There is no easy way to validate these - // are working but we can at least ensure we don't cause host boot - // to crash when running them. - TRACFBIN(g_trac_test,"Binary dump of trace descriptor", - g_trac_test,sizeof(trace_desc_t)); - - TRACDBIN(g_trac_test,"Unaligned Debug binary trace of the trace descriptor", - g_trac_test,9); - - // Be sure a NULL trace descriptor does not cause a failure - TRACFBIN(NULL,"This trace should never show up", - g_trac_test,sizeof(trace_desc_t)); - - // Be sure it handles a size of 0 - TRACFBIN(NULL,"This trace should never show up - 0 size", - g_trac_test,0); - } - } - - /** - * @test Test a mix of traces - */ - void testTracMix(void) - { - trace_desc_t *g_trac_test = NULL; - TRAC_INIT_BUFFER(&g_trac_test, "EXAMPLEMIX", 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - uint32_t l_size = 0; - - for(uint32_t i=0;i<100;i++) - { - - TRACFCOMP(g_trac_test, INFO_MRK"Validate all number types (c,u,X,d): %c %u 0x%X %d", - 'a',i,i+1,i+2); - - l_size = i % sizeof(trace_desc_t); - - TRACFBIN(g_trac_test,"Unaligned Binary dump of trace descriptor", - g_trac_test,l_size); - - TRACFCOMP(g_trac_test, "64 Bit Value Test - 0x123456789ABCDEF0: 0x%X", - 0x123456789ABCDEF0); - - } - } - } - - /** - * @test Test max component name size - */ - void testTracCompName(void) - { - trace_desc_t *g_trac_test = NULL; - char l_comp[] = "EXAMPLE89ABCDEFGHI"; - - TRAC_INIT_BUFFER(&g_trac_test, l_comp, 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - TRACFCOMP(g_trac_test, INFO_MRK"Testing max component name"); - } - } - - /** - * @test Test trace macros - */ - void testTracMacros(void) - { - trace_desc_t *g_trac_test = NULL; - char l_comp[] = "TEST"; - - #define __COMP_TD__ g_trac_test - #define __COMP_NAMESPACE__ "TRACE" - #define __COMP_CLASS__ "TraceTest" - #define __COMP_FN__ "testTracMacros" - - TRAC_INIT_BUFFER(&g_trac_test, l_comp, 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - TRAC_ENTER("testTracMacros"); - TRAC_ENTER_(); - TRAC_INF("Testing Info Mark"); - TRAC_ERR("Testing Error Mark"); - TRAC_EXIT("testTracMacros"); - TRAC_EXIT_(); - - DTRAC_ENTER("testTracMacros - Debug Trace"); - DTRAC_ENTER_(" - Debug Trace"); - DTRAC_INF("Testing Debug Info Mark"); - DTRAC_ERR("Testing Debug Error Mark"); - DTRAC_EXIT("testTracMacros - Debug Trace"); - DTRAC_EXIT_(" - Debug Trace"); - - STRAC_ENTER("testTracMacros - Strace"); - STRAC_ENTER_(" - Strace"); - STRAC_INF("Testing Strace Info Mark"); - STRAC_ERR("Testing Strace Error Mark"); - STRAC_EXIT("testTracMacros - Strace"); - STRAC_EXIT_(" - Strace"); - - } - - #undef __COMP_FN__ - #undef __COMP_CLASS__ - #undef __COMP_NAMESPACE__ - #undef __COMP_TD__ - } - - /** - * @test Test String Trace Interface - */ - void testTracString(void) - { - trace_desc_t *g_trac_test = NULL; - TRAC_INIT_BUFFER(&g_trac_test, "STRING", 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - TRACFCOMP(g_trac_test,"String at end '%s'", "Last"); - TRACFCOMP(g_trac_test,"%s: String at beginning", "FIRST"); - TRACFCOMP(g_trac_test,"Test '%s' string", "middle"); - - const char * str = "This is a \tlong string\nwith horizontal tab and newline."; - TRACFCOMP(g_trac_test,"Testing (c,u,s,X): %c, %u, %s 0x%X", 'b',0x11,str,10); - - TRACFCOMP(g_trac_test,"Testing string len 1: %s, %s, %s", "A", "B", "C"); - TRACFCOMP(g_trac_test,"Testing NULL string: %s", ""); - - TRACFCOMP(g_trac_test,"Testing string alignment: %s %s", "hello", "world"); - TRACFCOMP(g_trac_test,"Testing string alignment: %s %s", "Hostboot", "Software"); - TRACFCOMP(g_trac_test,"Testing string alignment: %s %s", "Hostboot2", "Software3"); - - TRACFCOMP(g_trac_test,"%s %s %s %s", "Four", "strings", "by", "themselves"); - - TRACFCOMP(g_trac_test,"Testing special characters: %s", - "?!@#$%^&*()\"/\'\\<>.,:;"); - - TRACFCOMP(g_trac_test,"Testing percent: %% %%%d %%%s 100%%", 50, "hello"); - - TRACFCOMP(g_trac_test, INFO_MRK"Testing all number types (s,c,u,X,d): %s, %c %u 0x%X %d", - "hello",'a',10,11,12); - TRACFCOMP(g_trac_test, "Testing all number types (c,u,s,X,d): %c %u %s 0x%X %d", - 'b',13,"world!",14,15); - TRACFCOMP(g_trac_test, "Testing all number types (c,u,X,d,s): %c %u 0x%X %d %s", - 'c',16,17,18,"Hostboot"); - TRACFCOMP(g_trac_test, "Testing all number types (s,c,s,u,s,X): %s %c %s %u %s 0x%X", - "Astring",'d',"Bstring12",19,"Cstring123",20); - } - } - - /** - * @test Test max number of buffers - */ - void testTracMaxBuffers(void) - { - trace_desc_t *g_trac_test = NULL; - char l_comp[8] = "TRACE"; - - for (uint32_t i=0; i < 26; i++) - { - sprintf (l_comp, "TRACE%d", i); - - g_trac_test = NULL; - TRAC_INIT_BUFFER(&g_trac_test, l_comp, 4096); - - if(g_trac_test == NULL) - { - TS_FAIL("g_trac_test was returned as NULL!"); - } - else - { - TRACFCOMP(g_trac_test, INFO_MRK"Testing max buffers %u", i); - } - } - } -}; - -#endif - diff --git a/src/usr/trace/trace.C b/src/usr/trace/trace.C deleted file mode 100644 index 8ef760533..000000000 --- a/src/usr/trace/trace.C +++ /dev/null @@ -1,1555 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/usr/trace/trace.C $ */ -/* */ -/* IBM CONFIDENTIAL */ -/* */ -/* COPYRIGHT International Business Machines Corp. 2011,2012 */ -/* */ -/* p1 */ -/* */ -/* Object Code Only (OCO) source materials */ -/* Licensed Internal Code Source Materials */ -/* IBM HostBoot Licensed Internal Code */ -/* */ -/* The source code for this program is not published or otherwise */ -/* divested of its trade secrets, irrespective of what has been */ -/* deposited with the U.S. Copyright Office. */ -/* */ -/* Origin: 30 */ -/* */ -/* IBM_PROLOG_END_TAG */ -/** - * @file trace.C - * - * @brief Implementation of class Trace - */ - - -/* TODO - * - Add support in for debug trace enable/disable - * - FORMAT_PRINTF support - * - %s support - * - Multiple buffer support - * - */ - -/******************************************************************************/ -// Includes -/******************************************************************************/ -#include <trace/interface.H> -#include <stdarg.h> -#include <stdlib.h> -#include <arch/ppc.H> -#include <kernel/console.H> -#include <kernel/pagemgr.H> -#include <limits.h> -#include <stdlib.h> -#include <sys/task.h> -#include <sys/sync.h> -#include <string_ext.h> -#include <util/align.H> -#include <assert.h> - -#include <trace/trace.H> -#include "tracedaemon.H" - - -/******************************************************************************/ -// Namespace -/******************************************************************************/ -namespace TRACE -{ - -/******************************************************************************/ -// Globals/Constants -/******************************************************************************/ - -const uint32_t TRAC_TIME_REAL = 0; // upper 32 = secs, lower 32 = microsecs -const uint32_t TRAC_TIME_50MHZ = 1; -const uint32_t TRAC_TIME_200MHZ = 2; -const uint32_t TRAC_TIME_167MHZ = 3; // 166666667Hz - - - - -// WARNING: Changing the size of the trace buffer name string requires a -// changing OFFSET_BUFFER_ADDRESS in src/build/debug/Hostboot/Trace.pm. -const uint32_t COMP_NAME_SIZE = 16; // includes NULL terminator, so 15 max - - -// Settings for the default buffer. Name must be upper case. -const char * const TRAC_DEFAULT_BUFFER_NAME = "DEFAULT"; -const uint64_t TRAC_DEFAULT_BUFFER_SIZE = 0x0800; //2KB - - -// The number of trace buffers. -// NOTE: This constant should only be changed to an even number for now. -// WARNING: Changing the count of buffers requires a co-req change -// in src/build/debug/Hostboot/Trace.pm which has this count hard coded. -const uint64_t TRAC_MAX_NUM_BUFFERS = 48; - -// An array of these structs accounts for all the trace buffers in Hostboot. -// WARNING: Changing the size of trace_desc_array requires a co-req change -// in src/build/debug/Hostboot/Trace.pm which has hard-coded the size of -// this structure. -typedef struct trace_desc_array -{ - char comp[COMP_NAME_SIZE]; // the buffer name - trace_desc_t * td_entry; // pointer to the buffer -}trace_desc_array_t; - -// Global: found in syms file and thus the dump. -trace_desc_array_t g_desc_array[TRAC_MAX_NUM_BUFFERS]; - - -// Set up a structure to hold information about the mixed-trace -// or continuous-trace buffer, aka tracBINARY. - -// WARNING: Changes to this structure will require co-req changes to -// src/build/debug/simics-debug-framework.py which contains the simics -// hap handler for extracting this buffer. -struct mixed_trace_info -{ - char * pBuffer; - uint64_t cbUsed; - uint32_t TriggerActive; - uint32_t LostTraceCount; -}; -typedef struct mixed_trace_info mixed_trace_info_t; -const uint64_t TRAC_BINARY_SIZE = 4096; -mixed_trace_info_t g_tracBinaryInfo[2]; - -struct vpo_con_trigger_t -{ - volatile uint64_t trig; // bit0 = trig signalling bit1:63 = trace buff addr - uint32_t len; // length of trace buffer with valid trace data. - uint32_t seq; -}; - -struct vpo_cont_support_t -{ - vpo_con_trigger_t triggers[2]; - uint64_t enable; // VPO script sets it to 2 - // SIMICS hap handler sets it to 0 - // Compiler sets it to 1 for Mbox -}; - -// This structure is monitored by VPO script. The enable variable is set -// at compile time to 1. The VPO script set the enable variable at the -// start to enable the continuous trace support for VPO. It then montiors the -// trigger active bit of each buffer and take action. -vpo_cont_support_t g_cont_trace_trigger_info = { { {0,0,0}, {0,0,1} }, 1 }; - -const uint64_t TRIGGER_ACTIVE_BIT = 0x8000000000000000; - -/******************************************************************************/ -// TracInit::TracInit() -/******************************************************************************/ -TracInit::TracInit(trace_desc_t **o_td, const char *i_comp,const size_t i_size) -{ - TRAC_INIT_BUFFER(o_td,i_comp,i_size); -} - -/******************************************************************************/ -// TracInit::~TracInit() -/******************************************************************************/ -TracInit::~TracInit() -{ -} - - -/******************************************************************************/ -// Trace::getTheInstance -/******************************************************************************/ -Trace& Trace::getTheInstance() -{ - return Singleton<Trace>::instance(); -} - -/******************************************************************************/ -// Trace::Trace -/******************************************************************************/ -Trace::Trace() -{ - mutex_init(&iv_trac_mutex); - - // compiler inits global vars to zero - // memset(g_desc_array, 0, sizeof(g_desc_array)); - - // fsp-trace convention expects a 2 in the first byte of tracBINARY - for (size_t i = 0; i < 2; i++) - { - g_tracBinaryInfo[i].pBuffer = - static_cast<char*>(malloc(TRAC_BINARY_SIZE)); - g_tracBinaryInfo[i].pBuffer[0] = 2; - g_tracBinaryInfo[i].cbUsed = 1; - g_tracBinaryInfo[i].TriggerActive = 0; - g_tracBinaryInfo[i].LostTraceCount = 0; - g_cont_trace_trigger_info.triggers[i].trig = - reinterpret_cast<uint64_t>(g_tracBinaryInfo[i].pBuffer); - } - - // if this code is running under simics, call the hap handler to set - // g_cont_trace_trigger_info.enable to 0. Otherwise, this will be noop - MAGIC_INSTRUCTION(MAGIC_CONTINUOUS_TRACE); - - // tracBINARY buffer appending is always on. - // TODO figure a way to control continuous trace on/off, perhaps - // unregister the hap handler for it. - iv_ContinuousTrace = 1; - - // select buffer0 initially - iv_CurBuf = 0; - // initialize seq number - iv_seqNum = 0; - - // Create the daemon. - iv_daemon = new TraceDaemon(); -} - -/******************************************************************************/ -// Trace::~Trace -/******************************************************************************/ -Trace::~Trace() -{ - // Delete the daemon. - delete iv_daemon; -} - - -/******************************************************************************/ -// trace_adal_init_buffer -/******************************************************************************/ -void Trace::initBuffer( trace_desc_t **o_td, - const char* i_comp, - size_t i_size ) -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - unsigned int i = 0; - char l_comp[COMP_NAME_SIZE] = {'\0'}; - - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ - - - // Limit buffer sizes to 2KB - if( i_size > TRAC_DEFAULT_BUFFER_SIZE ) - { - i_size = TRAC_DEFAULT_BUFFER_SIZE; - } - - // Limit component name to 15 characters. - if (strlen(i_comp) > (COMP_NAME_SIZE -1)) - { - memcpy(l_comp, i_comp, COMP_NAME_SIZE - 1); - } - else - { - strcpy(l_comp, i_comp); - } - - // Store buffer name internally in upper case - strupr(l_comp); - - // The page containing the trace-descriptor destination might not be - // loaded yet, so we write to it outside of the mutex to force a page - // fault to bring the page in. If we don't do this, we can end up with - // a dead-lock where this code is blocked due to a page-fault while - // holding the trace mutex, which in turn blocks the code that handles - // page faults. - *o_td = NULL; - - // CRITICAL REGION START - mutex_lock(&iv_trac_mutex); - - // Search through the descriptor array for the first unallocated buffer. - // The last buffer is the reserved default buffer for any component - // which didn't get its own buffer. - for (i = 0; i < (TRAC_MAX_NUM_BUFFERS - 1); i++) - { - if( 0 == strcmp(l_comp, g_desc_array[i].comp)) - { - // Buffer is already allocated for the given buffer name. - // Return the pointer to the buffer. - *o_td = g_desc_array[i].td_entry; - break; - } - else if ( '\0' == g_desc_array[i].comp[0] ) - { - // Found an unallocated buffer; use this one. - // Set the component name for the buffer - strcpy(g_desc_array[i].comp, l_comp); - - // Allocate memory for the trace buffer. - *o_td = g_desc_array[i].td_entry = - reinterpret_cast<trace_desc_t *>(malloc(i_size)); - - // Initialize the trace buffer. - initValuesBuffer( g_desc_array[i].td_entry, - g_desc_array[i].comp, - i_size ); - - break; - } - } - - - if ((TRAC_MAX_NUM_BUFFERS - 1) == i) - { - // We're out of buffers to allocate. - // Use the default buffer reserved for everyone else. - // Initialize only once - if ( '\0' == g_desc_array[i].comp[0] ) - { - // Set the component name for the buffer - strcpy(g_desc_array[i].comp, TRAC_DEFAULT_BUFFER_NAME); - - // Allocate memory for buffer - g_desc_array[i].td_entry = - reinterpret_cast<trace_desc_t *>(malloc(TRAC_DEFAULT_BUFFER_SIZE)); - - // Initialize the buffer header - initValuesBuffer(g_desc_array[i].td_entry, - g_desc_array[i].comp, - TRAC_DEFAULT_BUFFER_SIZE); - } - - // Return the default buffer - *o_td = g_desc_array[i].td_entry; - } - - mutex_unlock(&iv_trac_mutex); - // CRITICAL REGION END - - return; -} - -/******************************************************************************/ -// initValuesBuffer -/******************************************************************************/ -void Trace::initValuesBuffer( trace_desc_t *o_buf, - const char *i_comp, - size_t i_size ) -{ - // Initialize it to all 0's - memset(o_buf,0,i_size); - - o_buf->ver = TRACE_BUF_VERSION; - o_buf->hdr_len = sizeof(trace_buf_head_t); - o_buf->time_flg = TRAC_TIME_REAL; - o_buf->endian_flg = 'B'; // Big Endian - strcpy(o_buf->comp,i_comp); - o_buf->size = i_size; - o_buf->times_wrap = 0; - o_buf->next_free = sizeof(trace_buf_head_t); - - return; -} - -/******************************************************************************/ -// ManageContTraceBuffers -// This function manages the usage of the two ping-pong buffers for handling -// the continuous trace support. -// -// 1) Under VPO/VBU (g_cont_trace_trigger_info.enable = 2) -// The handshake between Hostboot code and VPO/VBU script is shown belows -// _______________________ -// Trigger_Avtive ____/ \__________ -// _________________________ -// ScomReg_Active _______/ \______ -// -// Hostboot code (this function) sets Trigger_Active, then ScomReg_Active, -// VPO/VBU script detects ScomReg_Active active, off-load the trigger buffer, -// and clears Trigger_Active. Hostboot code detects a trigger-state buffer -// with Trigger_Active cleared, reset ScomReg_Active. Two-level trigger is -// desired. The ScomReg trigger allows VPO script to sample at no expense -// of simclocks, thus avoiding spending extra simclocks associated with the -// flushing of L2/L3 to read the memory trigger. -// -// 2) Under Simics with hap handler (g_cont_trace_trigger_info.enable = 0) -// The handshake between Hostboot code and the hap handler is shown belows -// _______________________ -// Trigger_Avtive ____/ \__________ -// -// Hostboot code (this function) sets Trigger_Active, then invokes the hap -// handler. The hap handler off-loads the trigger buffer, and clears the -// Trigger_Active. -// -// 3) Under Simics/HW with FSP mbox (g_cont_trace_trigger_info.enable = 1) -// _______________________ -// Trigger_Avtive ____/ \__________ -// -// Hostboot code (this function) sets Trigger_Active, then schedule a thread -// to use mbox DMA MSG to off-load the trigger buffer to FSP, and clears the -// Trigger_Active. -// -/******************************************************************************/ -void Trace::ManageContTraceBuffers(uint64_t i_cbRequired) -{ - uint8_t l_AltBuf = (iv_CurBuf + 1) % 2; - bool needScomReset = false; - - // Reset TriggerActive if the buffer has been offloaded by VPO - // script when running under VBU Awan environment - for (size_t i = 0; (g_cont_trace_trigger_info.enable > 1) && (i < 2); i++) - { - if ((g_tracBinaryInfo[i].TriggerActive != 0) && - (!(g_cont_trace_trigger_info.triggers[i].trig & - TRIGGER_ACTIVE_BIT))) - { - g_tracBinaryInfo[i].TriggerActive = 0; - needScomReset = true; - } - } - - if (needScomReset) - { - msg_t* l_msg = msg_allocate(); - l_msg->type = TraceDaemon::UPDATE_SCRATCH_REG; - l_msg->data[0] = 0; - msg_send(iv_daemon->iv_msgQ, l_msg); - } - - // we should never have the current buffer in the trigger state - assert (g_tracBinaryInfo[iv_CurBuf].TriggerActive == 0); - - // current buffer is not in trigger state - // and adding this trace will exceed the size - if ((g_tracBinaryInfo[iv_CurBuf].cbUsed + i_cbRequired) - > TRAC_BINARY_SIZE) - { - // current buffer entering trigger state - g_tracBinaryInfo[iv_CurBuf].TriggerActive = 1; - - if (g_cont_trace_trigger_info.enable > 1) - { - if (g_tracBinaryInfo[l_AltBuf].TriggerActive == 1) - { - // If the alternate buffer's trigger is active, wait for a - // chance to get offloaded before it is reused. - uint64_t l_wait = 0x100000; - while (g_cont_trace_trigger_info.triggers[l_AltBuf].trig & - TRIGGER_ACTIVE_BIT) - { - if (--l_wait == 0) - { - break; - } - } - // If alternate buffer has been offloaded, exit trigger state. - if (l_wait != 0) - { - g_tracBinaryInfo[l_AltBuf].TriggerActive = 0; - } - } - - g_cont_trace_trigger_info.triggers[iv_CurBuf].seq = iv_seqNum++; - g_cont_trace_trigger_info.triggers[l_AltBuf].seq = iv_seqNum; - // Turn on the current buffer's trigger - g_cont_trace_trigger_info.triggers[iv_CurBuf].trig |= - TRIGGER_ACTIVE_BIT; - - msg_t* l_msg = msg_allocate(); - l_msg->type = TraceDaemon::UPDATE_SCRATCH_REG; - l_msg->data[0] = 0x13579BDF00000000; - l_msg->data[0] += (iv_seqNum * 0x100000000); - msg_send(iv_daemon->iv_msgQ, l_msg); - } - - // If the alternate buffer is in trigger state, move it out of - // the trigger state and keep track of lost trace count. - if (g_tracBinaryInfo[l_AltBuf].TriggerActive == 1) - { - g_tracBinaryInfo[l_AltBuf].LostTraceCount++; - g_tracBinaryInfo[l_AltBuf].TriggerActive = 0; - } - // Now switching to alternate buffer and reset the usage count - uint8_t l_cur = iv_CurBuf; - uint64_t l_len = g_tracBinaryInfo[l_cur].cbUsed; - iv_CurBuf = l_AltBuf; - g_tracBinaryInfo[iv_CurBuf].cbUsed = 1; - - // For FSP mbox method. - if (g_cont_trace_trigger_info.enable == 1) - { - msg_t* l_msg = msg_allocate(); - l_msg->type = TraceDaemon::SEND_TRACE_BUFFER; - l_msg->data[1] = TRAC_BINARY_SIZE; - l_msg->extra_data = malloc(TRAC_BINARY_SIZE); - memcpy( l_msg->extra_data, g_tracBinaryInfo[l_cur].pBuffer, l_len ); - msg_send(iv_daemon->iv_msgQ, l_msg); - g_tracBinaryInfo[l_cur].TriggerActive = 0; - } - - MAGIC_INSTRUCTION(MAGIC_CONTINUOUS_TRACE); - } -} - - -/******************************************************************************/ -// trace_adal_write_all -/******************************************************************************/ -void Trace::trace_adal_write_all(trace_desc_t *io_td, - const trace_hash_val i_hash, - const char * i_fmt, - const uint32_t i_line, - const int32_t i_type, ...) -{ - va_list args; - va_start(args, i_type); - - getTheInstance()._trace_adal_write_all(io_td, - i_hash, - i_fmt, - i_line, - i_type, args); - - va_end(args); -} - -/******************************************************************************/ -// trace_adal_write_all -/******************************************************************************/ -void Trace::_trace_adal_write_all(trace_desc_t *io_td, - const trace_hash_val i_hash, - const char * i_fmt, - const uint32_t i_line, - const int32_t i_type, va_list i_args) -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - uint32_t l_entry_size = 0; - uint32_t l_data_size= 0; - //trace_entire_entry_t l_entry; - trace_bin_entry_t l_entry; - uint64_t l_str_map = 0; - uint64_t l_char_map = 0; - uint64_t l_double_map = 0; - - - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ - - uint32_t num_args = 0; - uint32_t num_4byte_args = 0; //fsp-trace counts 8-byte args as 2 4-byte args - const char* _fmt = i_fmt; - - // Save a copy of input args because calling - // va_arg() on a va_list is a one-shot. - va_list l_args; - va_copy (l_args, i_args); - - - // Sum the sizes of the items in i_args in order to know how big to - // allocate the entry. - - for (size_t i = 0; i <= strlen(_fmt); i++) - { - if ('%' == _fmt[i]) - { - i++; - - if ('%' == _fmt[i]) - { - continue; - } - else if ('s' == _fmt[i]) - { - // Set flag to indicate argument is a string - l_str_map = l_str_map | (1 << num_args); - - // String counts as one 4-byte arg - num_args++; - num_4byte_args++; - - char * l_str = va_arg(i_args, char *); - size_t l_strLen = strlen(l_str); - - // Add to total size of number of arguments we're tracing - // and account for word alignment - l_data_size += l_strLen + 1; - l_data_size = ALIGN_4(l_data_size); - - //printk("Trace: STRING %s: strlen %d num_args %d l_data_size %d\n", - // l_str, static_cast<uint32_t>(l_strLen), - // num_args, l_data_size); - //printk("Trace: l_str_map 0x%16llX\n", - // static_cast<long long>(l_str_map)); - } - else if ('c' == _fmt[i]) - { - // Set flag to indicate argument is a char - l_char_map = l_char_map | (1 << num_args); - - // Increment arg counts - num_args++; - num_4byte_args++; - - // Retrieve the argument to increment to next one - uint32_t l_tmpData = va_arg(i_args, uint32_t); - - // Add to total size; data is word aligned - l_data_size += sizeof(l_tmpData); - } - else if (('e' == _fmt[i]) || ('f' == _fmt[i]) || ('g' == _fmt[i])) - { - // Set flag to indicate argument is a double - l_double_map = l_double_map | (1 << num_args); - - // Numbers count as two 4-byte arg - num_args++; - num_4byte_args += 2; - - // Retrieve the argument to increment to next one - double l_tmpData = va_arg(i_args,double); - - // Add to total size; data is word aligned - l_data_size += sizeof(l_tmpData); - } - else - { - // Numbers count as two 4-byte arg - num_args++; - num_4byte_args += 2; - - // Retrieve the argument to increment to next one - uint64_t l_tmpData = va_arg(i_args, uint64_t); - - // Add to total size; data is word aligned - l_data_size += sizeof(l_tmpData); - } - } - } - - - if((num_4byte_args <= TRAC_MAX_ARGS) && (io_td != NULL)) - { - // Fill in the entry structure - l_entry.stamp.tid = static_cast<uint32_t>(task_gettid()); - - // Length is equal to size of data - l_entry.head.length = l_data_size; - //l_entry.head.tag = TRACE_FIELDTRACE; - l_entry.head.tag = TRACE_COMP_TRACE; - l_entry.head.hash = i_hash; - l_entry.head.line = i_line; - - - // Calculate total space needed for the entry, which is a - // combination of the data size from above, the entry - // headers, and an overall length field. - l_entry_size = l_data_size + - sizeof(trace_entry_stamp_t) + - sizeof(trace_entry_head_t) + - // Allow for a uint32 at the end of the trace entry - // so parsers may walk the trace buffer backwards - sizeof(uint32_t); - - // Round up the size to the next word boundary - l_entry_size = ALIGN_4(l_entry_size); - - // Allocate buffer for the arguments we're tracing - void * l_buffer = NULL; - if (l_data_size) - { - l_buffer = malloc(l_data_size); - memset(l_buffer, 0, l_data_size); - } - char * l_ptr = static_cast<char *> (l_buffer); - - // Now copy the arguments to the buffer. - - - for (size_t i = 0; i < num_args; i++) - { - uint32_t l_strLen = 0; - - if (l_str_map & (1 << i)) - { - // Save string to buffer - strcpy(l_ptr, va_arg(l_args, char *)); - - //printk("Trace: Saved String %s Arg[%d]\n", l_ptr, static_cast<uint32_t>(i)); - - // Length = string length + NULL termination - l_strLen += (strlen(l_ptr) + 1); - - // Increment pointer to next word alignment - l_ptr += l_strLen; - l_ptr = reinterpret_cast<char *>( - ALIGN_4(reinterpret_cast<uint64_t>(l_ptr)) ); - - //printk("Trace::trace_adal_write_all - l_buffer %p l_ptr %p l_strLen %d\n", - // l_buffer, l_ptr, l_strLen); - //printk("Trace::trace_adal_write_all - num_args %d l_data_size %d l_entry_size %d\n", - // num_args, l_data_size, l_entry_size); - } - else if (l_char_map & (1 << i)) - { - // Save char to buffer & increment pointer (no need to align) - *(reinterpret_cast<uint32_t *>(l_ptr)) = va_arg(l_args, uint32_t); - l_ptr += sizeof(uint32_t); - - } - else if (l_double_map & (1 << i)) - { - // Save number to buffer & increment pointer (no need to align) - *(reinterpret_cast<double *>(l_ptr)) = va_arg(l_args, double); - - l_ptr += sizeof(double); - } - else - { - // Save number to buffer & increment pointer (no need to align) - *(reinterpret_cast<uint64_t *>(l_ptr)) = va_arg(l_args, uint64_t); - l_ptr += sizeof(uint64_t); - } - } - - va_end(l_args); - - // Write entry to the trace buffer. - - // CRITICAL REGION START - mutex_lock(&iv_trac_mutex); - - // time stamp the entry with time-base register - convertTime(&l_entry.stamp); - - // Update the entry count - io_td->te_count++; - - // First write the header - writeData(io_td, - static_cast<void *>(&l_entry), - sizeof(l_entry)); - - // Now write the actual data - if (l_data_size) - { - writeData(io_td, - l_buffer, - l_data_size); - } - - // Now write the size at the end - // Note that fsp-trace assumes this to be a 32 bit long word - writeData(io_td, - static_cast<void *>(&l_entry_size), - sizeof(l_entry_size)); - - - - // Write to the combined trace buffer, a stream of traces. - while (iv_ContinuousTrace) - { - // This entry requires this many bytes to fit. - uint64_t l_cbCompName = 1 + strlen( io_td->comp ); - uint64_t l_cbRequired = l_cbCompName + sizeof(l_entry) + - l_data_size; - - if (l_cbRequired > TRAC_BINARY_SIZE) - { - // caller is logging more binary data than the - // maximum size of the current tracBinary buffer. - // TODO need to increase the buffer size, or else - // document its limits. - break; - } - - ManageContTraceBuffers(l_cbRequired); - - // Copy the entry piecemeal to the destination. - char * l_pchDest = g_tracBinaryInfo[iv_CurBuf].pBuffer + - g_tracBinaryInfo[iv_CurBuf].cbUsed; - - // component name and its trailing nil byte - strcpy( l_pchDest, io_td->comp ); - l_pchDest += l_cbCompName; - - // trace entry - memcpy( l_pchDest, &l_entry, sizeof(l_entry)); - l_pchDest += sizeof(l_entry); - - // trace entry data - memcpy( l_pchDest, l_buffer, l_data_size ); - - // adjust for next time - g_tracBinaryInfo[iv_CurBuf].cbUsed += l_cbRequired; - - // maintain the buffer's actually used bytes for VPO script - if (g_cont_trace_trigger_info.enable > 1) - { - g_cont_trace_trigger_info.triggers[iv_CurBuf].len = - g_tracBinaryInfo[iv_CurBuf].cbUsed; - } - - // break from while() which was used much like an if() - break; - } - - - mutex_unlock(&iv_trac_mutex); - // CRITICAL REGION END - - // Free allocated memory - free(l_buffer); - } - - return; -} - -/******************************************************************************/ -// trace_adal_write_bin -/******************************************************************************/ -void Trace::trace_adal_write_bin(trace_desc_t *io_td,const trace_hash_val i_hash, - const uint32_t i_line, - const void *i_ptr, - const uint32_t i_size, - const int32_t type) -{ - getTheInstance()._trace_adal_write_bin(io_td, i_hash, - i_line, - i_ptr, - i_size, - type); -} - -/******************************************************************************/ -// trace_adal_write_bin -/******************************************************************************/ -void Trace::_trace_adal_write_bin(trace_desc_t *io_td,const trace_hash_val i_hash, - const uint32_t i_line, - const void *i_ptr, - const uint32_t i_size, - const int32_t type) -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - uint32_t l_entry_size = 0; - trace_bin_entry_t l_entry; - - /*---------------------------------------------------------------------- --*/ - /* Code */ - /*------------------------------------------------------------------------*/ - do - { - - if((io_td == NULL) || (i_ptr == NULL) || (i_size == 0)) - { - break; - } - - // Calculate total space needed - l_entry_size = sizeof(trace_entry_stamp_t); - l_entry_size += sizeof(trace_entry_head_t); - - // We always add the size of the entry at the end of the trace entry - // so the parsing tool can easily walk the trace buffer stack so we - // need to add that on to total size - l_entry_size += sizeof(uint32_t); - - // Now add on size for actual size of the binary data - l_entry_size += i_size; - - // Word align the entry - l_entry_size = ALIGN_4(l_entry_size); - - // Fill in the entry structure - l_entry.stamp.tid = static_cast<uint32_t>(task_gettid()); - - // Length is equal to size of data - l_entry.head.length = i_size; - l_entry.head.tag = TRACE_FIELDBIN; - l_entry.head.hash = i_hash; - l_entry.head.line = i_line; - - // We now have total size and need to reserve a part of the trace - // buffer for this - - // CRITICAL REGION START - mutex_lock(&iv_trac_mutex); - - - // time stamp the entry with time-base register - convertTime(&l_entry.stamp); - - // Increment trace counter - io_td->te_count++; - - // First write the header - writeData(io_td, - static_cast<void *>(&l_entry), - sizeof(l_entry)); - - // Now write the actual binary data - writeData(io_td, - i_ptr, - i_size); - - // Now write the size at the end - writeData(io_td, - static_cast<void *>(&l_entry_size), - sizeof(l_entry_size)); - - // Write to the combined trace buffer, a stream of traces. - // A while() here affords use of break to break out on - // an error condition. - while( iv_ContinuousTrace ) - { - // This entry requires this many bytes to fit. - uint64_t l_cbCompName = 1 + strlen( io_td->comp ); - uint64_t l_cbRequired = l_cbCompName + sizeof( l_entry ) + i_size; - - if( l_cbRequired > TRAC_BINARY_SIZE ) - { - // caller is logging more binary data than the - // maximum size of the current tracBinary buffer. - // TODO need to increase the buffer size, or else - // document its limits. - break; - } - - ManageContTraceBuffers(l_cbRequired); - - // Copy the entry piecemeal to the destination. - char * l_pchDest = g_tracBinaryInfo[iv_CurBuf].pBuffer + - g_tracBinaryInfo[iv_CurBuf].cbUsed; - - // component name and its trailing nil byte - strcpy( l_pchDest, io_td->comp ); - l_pchDest += l_cbCompName; - - // trace entry - memcpy( l_pchDest, &l_entry, sizeof(l_entry)); - l_pchDest += sizeof(l_entry); - - // trace entry data - memcpy( l_pchDest, i_ptr, i_size ); - - // adjust for next time - g_tracBinaryInfo[iv_CurBuf].cbUsed += l_cbRequired; - - // maintain the buffer's actually used bytes for VPO script - if (g_cont_trace_trigger_info.enable > 1) - { - g_cont_trace_trigger_info.triggers[iv_CurBuf].len = - g_tracBinaryInfo[iv_CurBuf].cbUsed; - } - - // break from while() which was used much like an if() - break; - } - - - // CRITICAL REGION END - mutex_unlock(&iv_trac_mutex); - - }while(0); - - return; -} - -/******************************************************************************/ -// writeData -/******************************************************************************/ -void Trace::writeData(trace_desc_t *io_td, - const void *i_ptr, - const uint32_t i_size) -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - uint32_t l_total_size = i_size; - void *l_buf_ptr = NULL; - uint32_t l_offset = 0; - uint64_t l_align = 0; - - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ - - do - { - - if(i_size > (io_td->size-sizeof(trace_buf_head_t))) - { - // unreasonable size, caller is asking to write something - // that is very nearly the size of the entire buffer - break; - } - - - if((io_td->next_free + l_total_size) > io_td->size) - { - // Does not fit entirely, write what fits, and wrap the buffer. - - // Get the pointer to current location in buffer - l_buf_ptr = reinterpret_cast<char *>(io_td) + io_td->next_free; - // Figure out the alignment - l_align = ALIGN_4(reinterpret_cast<uint64_t>(l_buf_ptr)) - - reinterpret_cast<uint64_t>(l_buf_ptr); - // Add on the alignment - l_buf_ptr = reinterpret_cast<void *>(reinterpret_cast<uint64_t> - (l_buf_ptr) + l_align); - // Ensure offset accounts for the alignment - l_offset = io_td->size-io_td->next_free - l_align; - // Copy in what fits - memcpy(l_buf_ptr,i_ptr,static_cast<size_t>(l_offset)); - - l_total_size -= l_offset; - - // Now adjust the main header of buffer - io_td->times_wrap++; - io_td->next_free = io_td->hdr_len; - } - - // Get the pointer to current location in buffer - l_buf_ptr = reinterpret_cast<char *>(io_td) + io_td->next_free; - // Figure out the alignment - l_align = ALIGN_4(reinterpret_cast<uint64_t>(l_buf_ptr)) - - reinterpret_cast<uint64_t>(l_buf_ptr); - // Add on the alignment - l_buf_ptr = reinterpret_cast<void *>(reinterpret_cast<uint64_t> - (l_buf_ptr) + l_align); - - memcpy(l_buf_ptr,reinterpret_cast<const char *>(i_ptr) + l_offset, - l_total_size); - - // Make sure size is correct for word alignment - // Note that this works with binary trace because only the binary data - // has the potential to be un-word aligned. If two parts of the binary - // trace had this problem then this code would not work. - // Note that fsp-trace will ignore garbage data in the unaligned areas. - l_total_size = ALIGN_4(l_total_size); - io_td->next_free += l_total_size; - - }while(0); - - return; - -} - -/******************************************************************************/ -// convertTime -/******************************************************************************/ -void Trace::convertTime(trace_entry_stamp_t *o_entry) -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ - - // TODO - Future Sprint will collect proc frequency and correctly - // calculate this. - uint64_t l_time = getTB(); - //o_entry->tbh = l_time && 0xFFFFFFFF00000000; - //o_entry->tbl = l_time && 0x00000000FFFFFFFF; - - // This basically supports SIMICS, but will look weird on real hw - o_entry->tbh = (l_time / 512000000); - o_entry->tbl = ((l_time - (o_entry->tbh * 512000000)) / 512); - -} - - - - -/******************************************************************************/ -// findTdByName -/******************************************************************************/ -trace_desc_t * Trace::findTdByName(const char *i_pName) -{ - trace_desc_t * l_td = NULL; - char l_comp[COMP_NAME_SIZE]; - - - uint64_t i = strlen(i_pName); - if ( i ) - { - if ( i > (COMP_NAME_SIZE -1)) - { - // Limit component name. - memcpy(l_comp, i_pName, COMP_NAME_SIZE - 1); - l_comp[ COMP_NAME_SIZE - 1 ] = 0; - } - else - { - strcpy( l_comp, i_pName ); - } - - // Use upper case. - strupr( l_comp ); - - // Lock critical section to access g_desc_array - mutex_lock(&iv_trac_mutex); - - // Search the buffers array - for(i=0; - (i < (TRAC_MAX_NUM_BUFFERS - 1)) && - (strlen(g_desc_array[i].comp) != 0); - i++) - { - if(0 == strcmp(l_comp, g_desc_array[i].comp)) - { - // Return this one. - l_td = g_desc_array[i].td_entry; - break; - } - } - - // Unlock critical section - mutex_unlock(&iv_trac_mutex); - - } - - return l_td; -} - - - - -/*****************************************************************************/ -// getBuffer() called by ErrlEntry.collectTrace() -// Return how many bytes copied to output buffer. -// If given a null pointer or zero buffer then return the full size -// of the buffer. -// -// Otherwise return zero on error; perhaps the component name/trace buffer -// name is not found, or maybe the size of buffer given is too small to even -// hold a trace buffer header. - -uint64_t Trace::getBuffer( const char * i_pComp, - void * o_data, - uint64_t i_size ) -{ - const char * l_pchEntry = NULL; // use this to walk the entries - const char * l_pchEntryEOL = NULL; // end of list of entries - const char * l_pchTraceBuffer = NULL; // source buffer, including header - const char * l_pchTraceData = NULL; // source data, just past header - const char * l_pchTraceEOB = NULL; // end of source buffer - trace_buf_head_t * l_pCallerHeader = NULL; // output buffer, including header - trace_desc_t * l_pDescriptor = NULL; - uint64_t l_cbWrap = 0; - uint64_t l_rc = 0; - - do - { - l_pDescriptor = findTdByName( i_pComp ); - if( NULL == l_pDescriptor ) - { - // trace buffer name not found - break; - } - - if( (o_data == NULL) || (i_size == 0 )) - { - // return how big is the buffer. - l_rc = l_pDescriptor->size; - break; - } - - // Round size down to nearest 4-byte boundary. - i_size = ALIGN_DOWN_4(i_size); - - - if( i_size < sizeof(trace_buf_head_t)) - { - // Need at least enough space for the header. - // printk("trace_get_buffer_partial: i_size too small"); - break; - } - - - // Caller's destination buffer starts with a trace_buf_head_t. - l_pCallerHeader = static_cast<trace_buf_head_t*>(o_data); - - - - if( i_size >= l_pDescriptor->size ) - { - // Caller's buffer is big enough to hold the whole buffer. - uint64_t l_copyCount = l_pDescriptor->size; - - // Get the lock - mutex_lock(&iv_trac_mutex); - - // If the buffer is not full, then the unused - // portion is just zeroes. Avoid copying the zeroes. - if( 0 == l_pDescriptor->times_wrap ) - { - // Buffer has never wrapped, so copy the - // data up to the next-free offset. - l_copyCount = l_pDescriptor->next_free; - } - - // Copy source buffer to caller's destination buffer - memcpy( o_data, l_pDescriptor, l_copyCount ); - - mutex_unlock(&iv_trac_mutex); - - // Update the header in the output buffer. - l_pCallerHeader->size = l_copyCount; - - l_rc = l_copyCount; - break; - } - - - - // Input buffer size is smaller than source buffer size. - - mutex_lock(&iv_trac_mutex); - - if((i_size >= l_pDescriptor->next_free) && (0 == l_pDescriptor->times_wrap)) - { - // The source buffer has not wrapped, - // and what is there fits into caller's buffer. - l_rc = l_pDescriptor->next_free; - memcpy( o_data, l_pDescriptor, l_rc ); - - mutex_unlock(&iv_trac_mutex); - - // Update the header in the output buffer. - l_pCallerHeader->size = l_rc; - break; - } - - - // Otherwise, walk the entries backwards because the word - // just prior to any entry is the length of the previous entry. - // Subtract this length from the current entry pointer to - // point to the previous entry. Wrap around as required. - - - // Trace descriptor points to base of source trace buffer. - l_pchTraceBuffer = reinterpret_cast<const char*>(l_pDescriptor); - - // Source trace data resides just past the header. - l_pchTraceData = reinterpret_cast<const char *>(l_pDescriptor+1); - - // EOB (end of buffer) of source trace buffer - l_pchTraceEOB = l_pchTraceBuffer + l_pDescriptor->size; - - // useful when calculating locations of wrapped data - l_cbWrap = l_pDescriptor->size - sizeof(trace_buf_head_t); - - // This is how much trace data caller's buffer can hold. - int l_cbToFill = i_size - sizeof(trace_buf_head_t); - - - - // Start at next_free, which is not an actual entry. - // It is where the next entry write will go when it comes. - // It also marks the end of the list (EOL). - l_pchEntryEOL = l_pchTraceBuffer + l_pDescriptor->next_free; - - - // Walk backwards through the entries, looking for a point - // such that when walking the source entries from that - // point forward, those entries will fit into the - // destination buffer. Because of the cases handled above, - // this walking will not loop around back to where we started - // within the source buffer. Otherwise there would have to be - // tests made for wrapping and sensing when l_pchEntry passes - // l_pchEntryEOL. Note that trace entry structures and payload - // data may be wrapped anywhere on a 4-byte bound. - - - // Start here and work backwards. - l_pchEntry = l_pchEntryEOL; - - - do - { - - if(( l_pchEntry == l_pchTraceData ) && (0 == l_pDescriptor->times_wrap)) - { - // Exit from this do loop with l_pchEntry the starting point. - // Probably not going to happen, because the non-wrap short - // buffer case was handled above. - // massert( 0 ); - break; - } - - - // Determine the size of the entry prior to l_pchEntry. Normally, - // this length is found in the 4-byte word just before the start of any - // entry. However, trace code may wrap any given trace entry - // anywhere on a 4-byte word. - - // massert( l_pchEntry >= l_pchTraceData ); - // massert( l_pchEntry < l_pchTraceEOB ); - // massert( 0 == (((uint64_t)(l_pchEntry)) & 3) ); - - // Length of previous entry is in prior 32-bit word. - const char * l_pchPreviousLength = l_pchEntry - sizeof(uint32_t); - - if( l_pchPreviousLength < l_pchTraceData ) - { - // I am at the start of the source data. Apply wrap byte count - // to find length up at the end of the buffer. - l_pchPreviousLength += l_cbWrap; - - // Source buffer must have wrapped. - // massert( l_pDescriptor->times_wrap ); - } - - // Dereference and get the length of previous entry. - const uint32_t * l_p32; - l_p32 = reinterpret_cast<const uint32_t*>(l_pchPreviousLength); - int l_cbPrevious = *l_p32; - - - - if(( l_cbToFill - l_cbPrevious ) < 0 ) - { - // This one is too much. l_pchEntry is the starting point. - // This is the regular exit point from this loop. - break; - } - - - // Given the length of the previous one, - // assign a new value to l_pchEntry - l_pchEntry -= l_cbPrevious; - - if( l_pchEntry < l_pchTraceData ) - { - // Wrap. - l_pchEntry += l_cbWrap; - } - - l_cbToFill -= l_cbPrevious; - // massert( l_cbToFill >= 0 ); - } - while( 1 ); - - - - // Having walked backwards, l_pchEntry is the starting point, - // All the entries forward of this point are supposed to fit - // into caller's data buffer. - - // Count how many copied from source to destination buffer. - int l_entriesCopied = 0; - int l_bytesCopied = sizeof( trace_buf_head_t ); - - // Set up destination header. - memcpy( l_pCallerHeader, l_pDescriptor, sizeof(trace_buf_head_t)); - - // Caller's destination area for trace entry data, just past the - // buffer header. - char * l_pchDest = reinterpret_cast<char*>(l_pCallerHeader+1); - - - while( l_pchEntry != l_pchEntryEOL ) - { - const trace_bin_entry_t * l_pEntry; - - // Calculate how many bytes make up this entry. Value - // goes into l_cbEntry; - int l_cbEntry; - - if( (l_pchEntry + sizeof(trace_bin_entry_t)) > l_pchTraceEOB ) - { - // This entry wraps. Copy this split-up - // trace_bin_entry_t to the callers destination buffer - // (save a malloc) then reference entry->head.length. - - // Copy this much from the end of the source trace buffer. - int l_cb = l_pchTraceEOB - l_pchEntry; - memcpy( l_pchDest, l_pchEntry, l_cb ); - - // Copy the rest from the start of data in the trace buffer. - int l_cbTheRest = sizeof( trace_bin_entry_t ) - l_cb; - memcpy( l_pchDest+l_cb, l_pchTraceData, l_cbTheRest ); - - // I just copied one of these into callers destination buffer. - l_pEntry = reinterpret_cast<trace_bin_entry_t*>(l_pchDest); - } - else - { - // Otherwise, point the entry into the source buffer. - l_pEntry = reinterpret_cast<const trace_bin_entry_t*>(l_pchEntry); - } - - // Compute length of this entry. entry->head.length is the actual - // length of the trace data, and has to rounded up to next 4-byte - // boundary. The extra uint32 is where the size is stored. - l_cbEntry = ALIGN_4(l_pEntry->head.length) + - sizeof( trace_bin_entry_t ) + - sizeof( uint32_t ); - - - if( (l_pchEntry + l_cbEntry) > l_pchTraceEOB ) - { - // It wraps. Copy this split-up entry to the - // callers buffer. - int l_cb = l_pchTraceEOB - l_pchEntry; - memcpy( l_pchDest, l_pchEntry, l_cb ); - - // Copy the rest - int l_cbTheRest = l_cbEntry - l_cb; - memcpy( l_pchDest + l_cb, l_pchTraceData, l_cbTheRest ); - - // Assign l_pchEntry to next entry - l_pchEntry = l_pchTraceData + l_cbTheRest; - } - else - { - // Copy to destination buffer in one go. - memcpy( l_pchDest, l_pchEntry, l_cbEntry ); - - // Assign l_pchEntry to next entry - l_pchEntry += l_cbEntry; - } - - l_bytesCopied += l_cbEntry; - // massert( 0 == ( l_bytesCopied & 3 )); - - // massert( l_pchEntry >= l_pchTraceData ); - // massert( l_pchEntry < l_pchTraceEOB ); - // massert( 0 == (((uint64_t)(l_pchEntry)) & 3) ); - - - // Increment new data destination pointer. - l_pchDest += l_cbEntry; - // massert( l_pchDest <= ((char*)l_pCallerHeader) + i_size ); - - // This will eventually go into destination header. - l_entriesCopied++; - - } - - // Done looking at source buffer stuff. - mutex_unlock(&iv_trac_mutex); - - // Finish the caller's trace buffer header. - l_pCallerHeader->times_wrap = 0; - l_pCallerHeader->te_count = l_entriesCopied; - l_pCallerHeader->next_free = l_bytesCopied; - l_pCallerHeader->size = l_bytesCopied; - - // Return how many bytes written to output buffer. - l_rc = l_bytesCopied; - } - while(0); - - return l_rc; -} - - -/******************************************************************************/ -// clearAllBuffers() -/******************************************************************************/ -void Trace::clearAllBuffers() -{ - - // obtain the big trace lock - mutex_lock(&iv_trac_mutex); - - // Walk the buffers array - for( unsigned int i=0; i < TRAC_MAX_NUM_BUFFERS; i++ ) - { - if( g_desc_array[i].comp[0] ) - { - // Named, thus in use. - - // Buffer sizes are variable, save the size of it. - uint32_t l_saveSize = g_desc_array[i].td_entry->size; - - initValuesBuffer( g_desc_array[i].td_entry, - g_desc_array[i].comp, - l_saveSize ); - } - } - - // release the lock - mutex_unlock(&iv_trac_mutex); -} - - -/******************************************************************************/ -// flushContBuffers() -/******************************************************************************/ -void Trace::flushContBuffers() -{ - getTheInstance()._flushContBuffers(); -} - -/******************************************************************************/ -// _flushContBuffers() -/******************************************************************************/ -void Trace::_flushContBuffers() -{ - mutex_lock(&iv_trac_mutex); - MAGIC_INSTRUCTION(MAGIC_CONTINUOUS_TRACE); - mutex_unlock(&iv_trac_mutex); -} - - -#if 0 -/******************************************************************************/ -// resetBuf - TODO -/******************************************************************************/ -int32_t Trace::resetBuf() -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - int64_t l_rc = 0; - //uint32_t l_num_des = 0; - //uint32_t i=0; - - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ - - // Get mutex so no one traces - - // TODO - l_rc = UTIL_MUTEX_GET(&iv_trac_mutex,TRAC_INTF_MUTEX_TIMEOUT); - if(l_rc != TX_SUCCESS) - { - printk("trace_reset_buf: Failure trying to get mutex"); - // Badness - } - else - { - l_num_des = sizeof(g_des_array) / sizeof(trace_descriptor_array_t); - - for(i=0;i<l_num_des;i++) - { - // Initialize the buffer - l_rc = trace_init_values_buffer(g_des_array[i].entry, - g_des_array[i].comp); - if(l_rc) - { - printk("trace_reset_buf: Failure in call to trace_init_values_buffer()"); - break; - } - } - } - - - // Always try to release even if fail above - // TODO - mutex - //UTIL_MUTEX_PUT(&iv_trac_mutex); - - return(l_rc); -} -#endif - -} // namespace diff --git a/src/usr/trace/tracebuffer.C b/src/usr/trace/tracebuffer.C deleted file mode 100644 index 7eda6ae25..000000000 --- a/src/usr/trace/tracebuffer.C +++ /dev/null @@ -1,77 +0,0 @@ -// IBM_PROLOG_BEGIN_TAG -// This is an automatically generated prolog. -// -// $Source: src/usr/trace/tracebuffer.C $ -// -// IBM CONFIDENTIAL -// -// COPYRIGHT International Business Machines Corp. 2011 -// -// p1 -// -// Object Code Only (OCO) source materials -// Licensed Internal Code Source Materials -// IBM HostBoot Licensed Internal Code -// -// The source code for this program is not published or other- -// wise divested of its trade secrets, irrespective of what has -// been deposited with the U.S. Copyright Office. -// -// Origin: 30 -// -// IBM_PROLOG_END -#include <limits.h> -#include "tracebuffer.H" - -TracePage* TracePage::setNext(TracePage* new_next) -{ - return __sync_val_compare_and_swap(&this->next, NULL, new_next); -} - -traceEntry* TracePage::claimEntry(size_t size) -{ - // this->size will eventually wrap and it will suddenly look like there - // is free space? - size_t position = __sync_fetch_and_add(&this->size, size); - if (position > (PAGE_SIZE - sizeof(TracePage))) - { - return NULL; - } - else - { - return (traceEntry*)&((uint8_t*)this)[sizeof(TracePage) + position]; - } -} - -traceEntry* TraceBuffer::claimEntry(size_t size) -{ - traceEntry* result = NULL; - TracePage* page = last; - - while (result == NULL) - { - result = page->claimEntry(size); - - if (NULL == result) - { - if (NULL != page->getNext()) - { - __sync_bool_compare_and_swap(&this->last, - page, page->getNext()); - page = page->getNext(); - } - else - { - TracePage* new_page = new (malloc(PAGE_SIZE)) TracePage(); - TracePage* prev = page; - - while(prev != NULL) - { - prev = prev->setNext(new_page); - } - } - } - } - - return result; -} diff --git a/src/usr/trace/tracebuffer.H b/src/usr/trace/tracebuffer.H deleted file mode 100644 index 90cc80f75..000000000 --- a/src/usr/trace/tracebuffer.H +++ /dev/null @@ -1,73 +0,0 @@ -// IBM_PROLOG_BEGIN_TAG -// This is an automatically generated prolog. -// -// $Source: src/usr/trace/tracebuffer.H $ -// -// IBM CONFIDENTIAL -// -// COPYRIGHT International Business Machines Corp. 2011 -// -// p1 -// -// Object Code Only (OCO) source materials -// Licensed Internal Code Source Materials -// IBM HostBoot Licensed Internal Code -// -// The source code for this program is not published or other- -// wise divested of its trade secrets, irrespective of what has -// been deposited with the U.S. Copyright Office. -// -// Origin: 30 -// -// IBM_PROLOG_END -#ifndef __TRACE_TRACEBUFFER_H -#define __TRACE_TRACEBUFFER_H - -#include <stdint.h> -#include <stdlib.h> -#include <new> -#include <limits.h> -#include <sys/task.h> - -struct traceEntry -{ - uint64_t component; - tid_t tid; - uint16_t length; - uint32_t hash; - uint64_t timestamp; - uint32_t line; - uint64_t values[0]; -}; - - -class TracePage -{ - public: - TracePage() : next(NULL), size(0) {}; - - TracePage* setNext(TracePage*); - TracePage* getNext() { return next; }; - traceEntry* claimEntry(size_t size); - - private: - TracePage* volatile next; - uint64_t size; -}; - -class TraceBuffer -{ - public: - TraceBuffer() - { - first = last = new (malloc(PAGE_SIZE)) TracePage(); - } - - traceEntry* claimEntry(size_t size); - - private: - TracePage* first; - TracePage* volatile last; -}; - -#endif diff --git a/src/usr/trace/tracedaemon.C b/src/usr/trace/tracedaemon.C deleted file mode 100644 index cbbb73e85..000000000 --- a/src/usr/trace/tracedaemon.C +++ /dev/null @@ -1,124 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/usr/trace/tracedaemon.C $ */ -/* */ -/* IBM CONFIDENTIAL */ -/* */ -/* COPYRIGHT International Business Machines Corp. 2012 */ -/* */ -/* p1 */ -/* */ -/* Object Code Only (OCO) source materials */ -/* Licensed Internal Code Source Materials */ -/* IBM HostBoot Licensed Internal Code */ -/* */ -/* The source code for this program is not published or otherwise */ -/* divested of its trade secrets, irrespective of what has been */ -/* deposited with the U.S. Copyright Office. */ -/* */ -/* Origin: 30 */ -/* */ -/* IBM_PROLOG_END_TAG */ -#include "tracedaemon.H" -#include <sys/task.h> -#include <targeting/common/commontargeting.H> -#include <vfs/vfs.H> -#include <devicefw/driverif.H> -#include <errl/errlentry.H> -#include <errl/errlmanager.H> - -namespace TRACE -{ - -TraceDaemon::TraceDaemon() : - iv_pMaster(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) -{ - iv_msgQ = msg_q_create(); - task_create(TraceDaemon::start, this); -} - -TraceDaemon::~TraceDaemon() -{ - // Send message to shutdown daemon thread. - msg_t* l_msg = msg_allocate(); - l_msg->type = DAEMON_SHUTDOWN; - msg_sendrecv(iv_msgQ, l_msg); - msg_free(l_msg); - - // Release message queue. - msg_q_destroy(iv_msgQ); -} - -void* TraceDaemon::start(void* i_self) -{ - reinterpret_cast<TraceDaemon *>(i_self)->run(); - return NULL; -}; - -void TraceDaemon::run() -{ - msg_t* l_msg = NULL; - - // Main daemon loop. - while(1) - { - // Get message from client. - l_msg = msg_wait(iv_msgQ); - - // Switch based on message type. - switch(l_msg->type) - { - case UPDATE_SCRATCH_REG: - updateScratchReg(l_msg->data[0]); - break; - - case SEND_TRACE_BUFFER: // TODO. - // Delete buffer for now. - free(l_msg->extra_data); - break; - - case DAEMON_SHUTDOWN: - // Respond to message and exit. - msg_respond(iv_msgQ, l_msg); - return; - }; - - if (msg_is_async(l_msg)) - { - // Delete async messages. - msg_free(l_msg); - } - else - { - // Respond to sync messages. - msg_respond(iv_msgQ, l_msg); - } - } - -} - -void TraceDaemon::updateScratchReg(uint64_t i_value) -{ - // Find master processor target. - if (iv_pMaster == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) - { - if (VFS::module_is_loaded("libtargeting.so") && - TARGETING::targetService().isInitialized()) - { - TARGETING::targetService().masterProcChipTargetHandle(iv_pMaster); - } - } - - // Write scratch register to requested value. - size_t l_size = sizeof(uint64_t); - errlHndl_t l_errl = deviceWrite(iv_pMaster, &i_value, l_size, - DEVICE_SCOM_ADDRESS(MB_SCRATCH_REGISTER_0)); - - if (l_errl) - { - errlCommit(l_errl, HBTRACE_COMP_ID); - } -} - -}; diff --git a/src/usr/trace/tracedaemon.H b/src/usr/trace/tracedaemon.H deleted file mode 100644 index 1dbc0935a..000000000 --- a/src/usr/trace/tracedaemon.H +++ /dev/null @@ -1,92 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/usr/trace/tracedaemon.H $ */ -/* */ -/* IBM CONFIDENTIAL */ -/* */ -/* COPYRIGHT International Business Machines Corp. 2012 */ -/* */ -/* p1 */ -/* */ -/* Object Code Only (OCO) source materials */ -/* Licensed Internal Code Source Materials */ -/* IBM HostBoot Licensed Internal Code */ -/* */ -/* The source code for this program is not published or otherwise */ -/* divested of its trade secrets, irrespective of what has been */ -/* deposited with the U.S. Copyright Office. */ -/* */ -/* Origin: 30 */ -/* */ -/* IBM_PROLOG_END_TAG */ - -#ifndef __TRACE_DAEMON_H -#define __TRACE_DAEMON_H - -#include <sys/msg.h> -#include <targeting/common/target.H> - -namespace TRACE -{ - /** @class TraceDaemon - * @brief Encapsulates the daemon functionality of trace. - * - * The main-line trace paths can send messages to this code to request - * actions, such as buffer offload (by mailbox or signalling a scratch - * register), to be done or (TODO) the FSP can send messages to it. - */ - class TraceDaemon - { - public: - /** Default Constructor - * - * Initializes class and starts daemon thread. - */ - TraceDaemon(); - - /** Default Destructor - * - * Shuts down daemon thread and releases mailbox queue. - */ - ~TraceDaemon(); - - /** Message types supported by the trace daemon. */ - enum SUPPORTED_MSG_TYPES - { - UPDATE_SCRATCH_REG, //< Update cont-trace scratch reg. - SEND_TRACE_BUFFER, //< Send buffer to FSP. - - DAEMON_SHUTDOWN, //< Shutdown daemon thread. - }; - - // Make trace class a friend so it can get the message queue. - friend class Trace; - - protected: - /** Message Queue */ - msg_q_t iv_msgQ; - - private: - /** Target for master processor */ - TARGETING::Target* iv_pMaster; - - /** SCOM address of scratch register. */ - static const uint32_t MB_SCRATCH_REGISTER_0 = 0x00050038; - - /** @brief Function to start daemon thread (using task_create). - * @param[in] Pointer to self. - */ - static void* start(void*); - - /** @brief Main daemon loop. */ - void run(); - - /** @brief Update a scratch register with the desired value. - * @param[in] i_value - Value to write to scratch register. - */ - void updateScratchReg(uint64_t i_value); - }; -}; - -#endif |