diff options
-rw-r--r-- | src/include/usr/errl/errlentry.H | 47 | ||||
-rw-r--r-- | src/include/usr/errl/errlreasoncodes.H | 1 | ||||
-rw-r--r-- | src/usr/errl/errlentry.C | 235 | ||||
-rw-r--r-- | src/usr/errl/test/errluserdetailtest.H | 183 |
4 files changed, 451 insertions, 15 deletions
diff --git a/src/include/usr/errl/errlentry.H b/src/include/usr/errl/errlentry.H index 2d36218ae..6ac856237 100644 --- a/src/include/usr/errl/errlentry.H +++ b/src/include/usr/errl/errlentry.H @@ -487,6 +487,27 @@ public: const uint64_t i_max = KILOBYTE); /** + * @brief Remove the duplicate trace entries from user detail data + * When multiple calls to collectTrace() on the same component id occur a + * new user detail section is created each time. This function removes the + * duplicates created from that process. + * + * @algorithm A high level overview of what the code does is as follows: + * * Iterate through the iv_SectionVector which contains all UD + * sections and add each trace_bin_entry_t to a vector corresponding + * to each component id found in iv_SectionVector. + * * For each vector in the map of component id vectors + * * sort the collection of traces by timestamp and hash + * * call unique where the same timestamp and hash are considered + * to be equivalent trace entries to be removed. + * * Put all remaining trace entries into a new, consolidated trace + * UD section. + * * Remove the old trace UD sections from iv_SectionVector + * * Add the new consolidated trace UD sections to the iv_SectionVector + */ + void removeDuplicateTraces(void); + + /** * @brief Remove the back trace user detail data * When an error log is constructed, the back trace is automatically * captured. This function removes the backtrace. This should be used when @@ -896,6 +917,8 @@ private: ErrlEntry(const ErrlEntry& i_right); ErrlEntry& operator=(const ErrlEntry& i_right); + + private: // Data Members ErrlPrvt iv_Private; // private header object @@ -909,20 +932,22 @@ private: // hostboot. errlTermState_t iv_termState; - // when true, the severity has been set "final" and can not be changed. - bool iv_sevFinal; + // when true, the severity has been set "final" and can not be changed. + bool iv_sevFinal; + + //when true, the current error log will not be saved to PNOR, sent to the + //BMC, or displayed in the console + bool iv_skipShowingLog; + + // when true, send this special type of eSEL to the BMC + // This is used to send OCC informational errors up to BMC + bool iv_eselCallhomeInfoEvent; - //when true, the current error log will not be saved to PNOR, sent to the - //BMC, or displayed in the console - bool iv_skipShowingLog; + // when true, if error log is committed, then the error log is tracked so + // doShutdown can request that a HB dump be done. + bool iv_doHbDump; - // when true, send this special type of eSEL to the BMC - // This is used to send OCC informational errors up to BMC - bool iv_eselCallhomeInfoEvent; - // when true, if error log is committed, then the error log is tracked so - // doShutdown can request that a HB dump be done. - bool iv_doHbDump; }; diff --git a/src/include/usr/errl/errlreasoncodes.H b/src/include/usr/errl/errlreasoncodes.H index c77e1de2a..56f9b7f87 100644 --- a/src/include/usr/errl/errlreasoncodes.H +++ b/src/include/usr/errl/errlreasoncodes.H @@ -58,6 +58,7 @@ namespace ERRORLOG ERRL_TEST_LOGREGISTER_UD = ERRL_COMP_ID | 0x05, ERRL_TEST_CALLOUT_UD = ERRL_COMP_ID | 0x06, ERRL_CORE_EX_TARGET_NULL = ERRL_COMP_ID | 0x07, + ERRL_TEST_DUPLICATE_TRACE = ERRL_COMP_ID | 0x08, //........ ERRL_LAST_ERR = ERRL_COMP_ID | 0xFF }; diff --git a/src/usr/errl/errlentry.C b/src/usr/errl/errlentry.C index d16ba7dab..b5df8a071 100644 --- a/src/usr/errl/errlentry.C +++ b/src/usr/errl/errlentry.C @@ -36,6 +36,7 @@ #include <stdlib.h> #include <string.h> #include <map> +#include <algorithm> #include <hbotcompid.H> #include <errl/errlentry.H> #include <errl/errlmanager.H> @@ -47,6 +48,10 @@ #include <errl/errludattribute.H> #include <errl/errludstate.H> #include <trace/interface.H> + +#include "../trace/entry.H" +#include <util/align.H> + #include <arch/ppc.H> #include <hwas/common/hwasCallout.H> #include <hwas/common/deconfigGard.H> @@ -292,7 +297,7 @@ bool ErrlEntry::collectTrace(const char i_name[], const uint64_t i_max) return l_rc; } -//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// void ErrlEntry::removeBackTrace() { @@ -1645,14 +1650,19 @@ uint64_t ErrlEntry::flatten( void * o_pBuffer, } } // for + // Before the trace UD sections are flattened, make sure there are no + // duplicates. + removeDuplicateTraces(); + for(it = iv_SectionVector.begin(); - (it != iv_SectionVector.end()) && (l_flatSize != 0); + (it != iv_SectionVector.end()) && (l_flatSize != 0); it++) { // If UD section is a trace. if( (FIPS_ERRL_COMP_ID == (*it)->iv_header.iv_compId) && (FIPS_ERRL_UDT_HB_TRACE == (*it)->iv_header.iv_sst) ) { + l_cb = (*it)->flatten( pBuffer, l_sizeRemaining ); if( 0 == l_cb ) { @@ -1794,6 +1804,227 @@ std::vector<void*> ErrlEntry::getUDSections(compId_t i_compId, return copy_vector; } + +void ErrlEntry::removeDuplicateTraces() +{ + // Define a custom comparator function for std::map.find() + struct mapComparator + { + bool operator()(const char* a, const char * b) const + { + return strcmp(a, b) < 0; + } + }; + + // map of component id and corresponding trace entries. + std::map<const char *, std::vector<TRACE::trace_bin_entry_t*>*, + mapComparator> traceUD_map; + auto it = traceUD_map.end(); + + uint64_t l_flatSize = flattenedSize(); + + // vector that will hold all of the trace UD sections + // that are free of duplicates. + std::vector<ErrlUD*> l_uniqueTraceUDVector; + + + // Iterate through iv_SectionVector and create a map of all unique + // component ids and their corresponding trace entries. + for(auto sectionVectorIt = iv_SectionVector.begin(); + (sectionVectorIt != iv_SectionVector.end()) && (l_flatSize != 0); + ++sectionVectorIt) + { + // If UD section is a trace. + if( (FIPS_ERRL_COMP_ID == (*sectionVectorIt)->iv_header.iv_compId) + && (FIPS_ERRL_UDT_HB_TRACE == (*sectionVectorIt)->iv_header.iv_sst) ) + { + char* l_data = static_cast<char*>((*sectionVectorIt)->data()); + + TRACE::trace_buf_head_t* l_trace_buf_head = + reinterpret_cast<TRACE::trace_buf_head_t*>(l_data); + + // Look for the component id in the map to insert trace entries + // or insert a new component id into the map to insert trace entries + const char* l_compName = l_trace_buf_head->comp; + + it = traceUD_map.find(l_compName); + + if (traceUD_map.end() == it) + { + traceUD_map[l_compName] = + new std::vector<TRACE::trace_bin_entry_t*>; + it = traceUD_map.find(l_compName); + } + + // Add all trace entries to map for the current component id. + l_data += l_trace_buf_head->hdr_len; + + for (size_t traceCount = 0; traceCount < l_trace_buf_head->te_count; + traceCount++) + { + TRACE::trace_bin_entry_t* l_trace_entry = + reinterpret_cast<TRACE::trace_bin_entry_t*>(l_data); + + it->second->push_back(l_trace_entry); + + // fsp-trace entries have an extra 4 bytes at the end of them + // hence the sizeof(uint32_t) + l_data += sizeof(TRACE::trace_bin_entry_t) + + ALIGN_8(l_trace_entry->head.length) + + sizeof(uint32_t); + } + } + } + + // Iterate through the map to apply duplicate pruning to all component ids + // found in iv_SectionVector + for (auto const& it : traceUD_map) + { + // Sort the vector by timestamp and hash + std::sort(it.second->begin(), it.second->end(), + // Define a lambda comparator function for sorting criteria + [](const TRACE::trace_bin_entry_t* a, + const TRACE::trace_bin_entry_t* b) + { + // a goes before b if a's timestamp is less than b's. + // If they are equal then compare the hash values. + bool result = false; + if (a->stamp.tbh < b->stamp.tbh) + { + result = true; + } + else if ((a->stamp.tbh == b->stamp.tbh) + && (a->stamp.tbl < b->stamp.tbl)) + { + result = true; + } + else if ((a->stamp.tbh == b->stamp.tbh) + && (a->stamp.tbl == b->stamp.tbl) + && (a->head.hash < b->head.hash)) + { + result = true; + } + return result; + }); + + // Call unique to prune the duplicate trace entries + auto newEndIt = std::unique(it.second->begin(), it.second->end(), + // Define a lambda predicate function for duplicate criteria + [](const TRACE::trace_bin_entry_t* a, + const TRACE::trace_bin_entry_t* b) + { + // a is equivalent to b if a's timestamp is the same as + // b's and their hashes are the same. + bool result = false; + if ((a->stamp.tbh == b->stamp.tbh) + && (a->stamp.tbl == b->stamp.tbl) + && (a->head.hash == b->head.hash)) + { + result = true; + } + return result; + }); + + it.second->resize(std::distance(it.second->begin(), newEndIt)); + + // Calculate the size of the buffer that will hold all remaining + // trace entries in the new UD section + size_t uniqueSize = sizeof(TRACE::trace_buf_head_t); + + for (auto uniqueIt = it.second->begin(); uniqueIt != it.second->end(); + ++uniqueIt) + { + uniqueSize += sizeof(TRACE::trace_bin_entry_t) + + ALIGN_8((*uniqueIt)->head.length) + + sizeof(uint32_t); + } + + + // Create a new buffer for the new UD section from the vector of traces + // for this component id. + TRACE::trace_buf_head_t* header = nullptr; + char* l_pBuffer = new char[ uniqueSize ](); + size_t l_pos = 0; + + // Write the header info to the buffer. + // This header info was chosen based on the code that is found in + // Buffer::getTrace() if that code is changed in the future those + // changes will need to be reflected here. + header = reinterpret_cast<TRACE::trace_buf_head_t*>(&l_pBuffer[l_pos]); + + memset(header, '\0', sizeof(TRACE::trace_buf_head_t)); + + header->ver = TRACE::TRACE_BUF_VERSION; + header->hdr_len = sizeof(TRACE::trace_buf_head_t); + header->time_flg = TRACE::TRACE_TIME_REAL; + header->endian_flg = 'B'; + memcpy(&header->comp[0], it.first, TRAC_COMP_SIZE); + header->times_wrap = 0; + header->te_count = it.second->size(); + header->size = uniqueSize; + header->next_free = uniqueSize; + + l_pos += header->hdr_len; + + // Copy the trace entries to the buffer + for (auto uniqueIt = it.second->begin(); uniqueIt != it.second->end(); + ++uniqueIt) + { + // fsp-traces have an extra 4 bytes. Hence the sizeof(uint32_t) + size_t entrySize = sizeof(TRACE::trace_bin_entry_t) + + ALIGN_8((*uniqueIt)->head.length) + + sizeof(uint32_t); + + memcpy(&l_pBuffer[l_pos], (*uniqueIt), entrySize); + + l_pos += entrySize; + + } + + ErrlUD* l_udSection = new ErrlUD( l_pBuffer, + uniqueSize, + FIPS_ERRL_COMP_ID, + FIPS_ERRL_UDV_DEFAULT_VER_1, + FIPS_ERRL_UDT_HB_TRACE ); + + l_uniqueTraceUDVector.push_back(l_udSection); + + delete[] l_pBuffer; + delete it.second; + } + + // Remove old trace UD sections + auto sectionVectorIt = iv_SectionVector.begin(); + while(sectionVectorIt != iv_SectionVector.end()) + { + // If UD section is a trace. + if( (FIPS_ERRL_COMP_ID == (*sectionVectorIt)->iv_header.iv_compId) + && (FIPS_ERRL_UDT_HB_TRACE == (*sectionVectorIt)->iv_header.iv_sst)) + { + // Remove the ErrlUD* at this position + delete (*sectionVectorIt); + // Erase this entry from the vector + sectionVectorIt = iv_SectionVector.erase(sectionVectorIt); + } + else + { + ++sectionVectorIt; + } + + } + + // Add new trace UD sections + for(auto it = l_uniqueTraceUDVector.begin(); + it != l_uniqueTraceUDVector.end(); + ++it) + { + iv_SectionVector.push_back((*it)); + } + +} + + + /** * @brief Check if the severity of this log indicates it is * customer visible, note this ignores any override flags that diff --git a/src/usr/errl/test/errluserdetailtest.H b/src/usr/errl/test/errluserdetailtest.H index 204951e38..4d3ad6db4 100644 --- a/src/usr/errl/test/errluserdetailtest.H +++ b/src/usr/errl/test/errluserdetailtest.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2016 */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -37,18 +37,19 @@ #include <errl/errlentry.H> #include <errl/errluserdetails.H> #include <errl/errlreasoncodes.H> +#include <errl/errlud.H> #include <errl/errludstring.H> #include <errl/errludbacktrace.H> #include <errl/errludtarget.H> #include <errl/errludlogregister.H> #include <errl/errludcallout.H> #include <errl/errludattribute.H> +#include <errl/hberrltypes.H> #include <targeting/common/targetservice.H> #include <targeting/common/iterators/rangefilter.H> #include <targeting/common/predicates/predicates.H> #include <targeting/common/util.H> #include <hwas/common/deconfigGard.H> - #include <devicefw/driverif.H> using namespace ERRORLOG; @@ -59,6 +60,184 @@ public: // Note that errlUserDetailsTarget is tested in the targeting unit test + + void testRemoveDuplicateTraces(void) + { + const size_t NUM_TRACE_ENTRIES = 5, NUM_COMPONENTS = 4; + typedef std::vector<void*> pVoidVec_t; + + trace_desc_t* trac_testRmDupTrac1_trace = nullptr; + TRAC_INIT(&trac_testRmDupTrac1_trace, "TEST_TRACE1", 2*KILOBYTE); + + trace_desc_t* trac_testRmDupTrac2_trace = nullptr; + TRAC_INIT(&trac_testRmDupTrac2_trace, "TEST_TRACE2", 2*KILOBYTE); + + trace_desc_t* trac_testRmDupTrac3_trace = nullptr; + TRAC_INIT(&trac_testRmDupTrac3_trace, "TEST_TRACE3", 2*KILOBYTE); + + trace_desc_t* trac_testRmDupTrac4_trace = nullptr; + TRAC_INIT(&trac_testRmDupTrac4_trace, "TEST_TRACE4", 2*KILOBYTE); + + // Test Case 1: Collect traces five times from the same component and + // verify no duplicates remain. + + ErrlEntry* l_err1 = new ErrlEntry( ERRL_SEV_INFORMATIONAL, + ERRL_USERDATA_TEST_MOD_ID, + ERRL_TEST_DUPLICATE_TRACE, + 0x1234567890, + 0x9876543210 ); + // Write traces to comp 1 + for (size_t i = 0; i < NUM_TRACE_ENTRIES; ++i) + { + TRACFCOMP(trac_testRmDupTrac1_trace, "Trace %d.", (i+1)); + } + + for (size_t i = 0; i < 5; ++i) + { + l_err1->collectTrace("TEST_TRACE1"); + } + + l_err1->removeDuplicateTraces(); + + pVoidVec_t traceUDSections_case1 = + l_err1->getUDSections(FIPS_ERRL_COMP_ID, FIPS_ERRL_UDT_HB_TRACE); + + errlCommit(l_err1, CXXTEST_COMP_ID); + + // ErrlEntry::removeDuplicateTraces() should have removed all duplicates + // and combined all trace UD sections into one section. If that isn't + // case then this test case should fail. + if(traceUDSections_case1.size() != 1) + { + TS_FAIL("The number of trace UD sections was != 1"); + } + + for(auto it = traceUDSections_case1.begin(); + it != traceUDSections_case1.end(); ++it) + { + TRACE::trace_buf_head_t* header = + reinterpret_cast<TRACE::trace_buf_head_t*>((*it)); + + if (header->te_count != NUM_TRACE_ENTRIES) + { + TS_FAIL("The number of trace entries was != NUM_TRACE_ENTRIES"); + } + } + + // Test Case 2: Collect from several components and verify that none + // of the traces were removed. + + ErrlEntry* l_err2 = new ErrlEntry( ERRL_SEV_INFORMATIONAL, + ERRL_USERDATA_TEST_MOD_ID, + ERRL_TEST_DUPLICATE_TRACE, + 0x1234567890, + 0x9876543210 ); + + // Write traces to comp 2 + for (size_t i = 0; i < NUM_TRACE_ENTRIES; ++i) + { + TRACFCOMP(trac_testRmDupTrac2_trace, "Trace %d.", (i+1)); + } + + // Write traces to comp 3 + for (size_t i = 0; i < NUM_TRACE_ENTRIES; ++i) + { + TRACFCOMP(trac_testRmDupTrac3_trace, "Trace %d.", (i+1)); + } + + // Write traces to comp 4 + for (size_t i = 0; i < NUM_TRACE_ENTRIES; ++i) + { + TRACFCOMP(trac_testRmDupTrac4_trace, "Trace %d.", (i+1)); + } + + + // Collect the traces for each one once. + l_err2->collectTrace("TEST_TRACE1"); + l_err2->collectTrace("TEST_TRACE2"); + l_err2->collectTrace("TEST_TRACE3"); + l_err2->collectTrace("TEST_TRACE4"); + + l_err2->removeDuplicateTraces(); + + // Get the trace UD sections + pVoidVec_t traceUDSections_case2 = + l_err2->getUDSections(FIPS_ERRL_COMP_ID, FIPS_ERRL_UDT_HB_TRACE); + + // Commit the error log + errlCommit(l_err2, CXXTEST_COMP_ID); + + // Since the ErrlEntry::collectTrace() function creates a new UD section + // on each call to it there should only be NUM_COMPONENTS trace UD + // sections returned from ErrlEntry::getUDSections(). If that isn't the + // case then something unexpected happened and this test case should + // fail. + if(traceUDSections_case2.size() != NUM_COMPONENTS) + { + TS_FAIL("The number of trace UD sections was != NUM_COMPONENTS."); + } + + // If ErrlEntry::removeDuplicateTraces() worked then none of the trace + // entries should be missing in any of the components. + for(auto it = traceUDSections_case2.begin(); + it != traceUDSections_case2.end(); it++) + { + TRACE::trace_buf_head_t* header = + reinterpret_cast<TRACE::trace_buf_head_t*>((*it)); + + if (header->te_count != NUM_TRACE_ENTRIES) + { + TS_FAIL("The number of trace entries was != NUM_TRACE_ENTRIES"); + } + } + + // Test Case 3: Collect from a component once, write a new trace to that + // component, and then collect from it several times. + + ErrlEntry* l_err3 = new ErrlEntry( ERRL_SEV_INFORMATIONAL, + ERRL_USERDATA_TEST_MOD_ID, + ERRL_TEST_DUPLICATE_TRACE, + 0x1234567890, + 0x9876543210 ); + + l_err3->collectTrace("TEST_TRACE1"); + + TRACFCOMP(trac_testRmDupTrac1_trace, "A New Trace."); + + for (size_t i = 0; i < 5; ++i) + { + l_err3->collectTrace("TEST_TRACE1"); + } + + l_err3->removeDuplicateTraces(); + + pVoidVec_t traceUDSections_case3 = + l_err3->getUDSections(FIPS_ERRL_COMP_ID, FIPS_ERRL_UDT_HB_TRACE); + + errlCommit(l_err3, CXXTEST_COMP_ID); + + // ErrlEntry::removeDuplicateTraces() should have removed all duplicates + // and combined all trace UD sections into one section. If that isn't + // case then this test case should fail. + if(traceUDSections_case3.size() != 1) + { + TS_FAIL("The number of trace UD sections was != 1"); + } + + for(auto it = traceUDSections_case3.begin(); + it != traceUDSections_case3.end(); ++it) + { + TRACE::trace_buf_head_t* header = + reinterpret_cast<TRACE::trace_buf_head_t*>((*it)); + + if (header->te_count != (NUM_TRACE_ENTRIES + 1)) + { + TS_FAIL("The number of trace entries was != 6"); + } + } + } + + /** * @test testString - Capture a String in an error log */ |