diff options
Diffstat (limited to 'src/usr/errl/parser/errlparser.C')
| -rw-r--r-- | src/usr/errl/parser/errlparser.C | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/src/usr/errl/parser/errlparser.C b/src/usr/errl/parser/errlparser.C new file mode 100644 index 000000000..739e65558 --- /dev/null +++ b/src/usr/errl/parser/errlparser.C @@ -0,0 +1,701 @@ +/** + * @file errlparser.C + * + * @brief Parse and display committed error logs. Enter + * errlparser ? (or -? or -h or --help) to print help. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <string> +using namespace std; + + +#include <errl/errltypes.H> +#include <hbotcompid.H> +using namespace ERRORLOG; + + +#define USAGE "\ +Usage:\n\ +\n\ +errlparser <imagefile> <symsfile> [-l | -d <logid>]\n\ +\n\ +Provide L3 memory image file and its hbicore*.syms file.\n\ + -l summarize all error logs (default)\n\ + -d id print detail from specific error log\n\ +\n\ +Contact: Monte Copeland\n\ +" + +//------------------------------------------------------------------------ +// Stop the program with a message. This message is often USAGE. Since +// this program will be spawned from traceHB.py, I think we're better +// served sending the error message to stdout, not stderr. I don't +// think Simics is piping stderr to its console. +void halt( char * msg ) +{ + fprintf( stdout, "%s", msg ); + + // exit the process with a non-zero process exit level. + exit(2); +} + + + + +//------------------------------------------------------------------------- +// The file src/include/usr/hbotcompid.H provide a mapping from compId_t +// to component name. +// that maps comp id to a string label for it. +struct _errlcompname +{ + char * pszName; + uint32_t value; +}; +typedef _errlcompname ERRLCOMPNAME_t; + +ERRLCOMPNAME_t g_errlcompnames[] = { +// comps.C generated at build time by a script +// that parses data from src/include/usr/hbotcompid.H. +// Refer to src/usr/errl/parser/makefile +#include <comps.C> +}; + +// Given a reason code which has a comp id mangled into +// it, return a char* to the component name. Return +// null if not found, which printf seems to handle +// OK by printing (null). +char * FindComp( uint16_t reasoncode ) +{ + char * pch = NULL; + uint32_t id = (reasoncode & 0xFF00 ); + int c = sizeof( g_errlcompnames ) / sizeof( g_errlcompnames[0] ); + for( int i = 0; i < c; i++ ) + { + if( id == g_errlcompnames[i].value ) + { + pch = g_errlcompnames[i].pszName; + break; + } + } + return pch; +} + + + +//------------------------------------------------------------ +// Print a hex dump of the input buffer to the output file +// given, probably stdout. + +int FormatBytes( FILE * f, char * pchInput, int count, int indent ) +{ + char szChars[ 80 ]; + char szHex[ 80 ]; + char szOffset[ 64 ]; + char szWork[ 256 ]; + int i; + unsigned int ul; + char * pch; + char * pchLine; + int cb; + char * pszIndent = NULL; + + + + pszIndent = static_cast<char*>(malloc( indent+1 )); + memset( pszIndent, 0, indent+1 ); + memset( pszIndent, ' ', indent ); + + + + pchLine = pchInput; + + + while( pchLine < pchInput + count ) + { + /* current offset */ + ul = pchLine - pchInput; + + sprintf( szOffset, "%08X", ul ); + + memset( szHex, ' ', sizeof( szHex )); + pch = szHex; + + cb = ((pchInput+count)-pchLine) > 16 ? 16 : ((pchInput+count)-pchLine); + + for( i = 0; i < cb; i++ ) + { + ul = (unsigned char) pchLine[ i ]; + + sprintf( szWork, "%02x", ul ); + memcpy( pch, szWork, 2 ); + + pch += 3; + } + + szHex[ 23 ] = '-'; + szHex[ 48 ] = 0; + + memset( szChars, 0, sizeof( szChars )); + for( i = 0; i < cb; i++ ) + { + szChars[i] = '.'; + + int t = pchLine[i]; + + if( t > 31 ) + { + szChars[i] = pchLine[ i ]; + } + } + + sprintf( szWork, "%s %s %s", szOffset, szHex, szChars ); + fprintf( f, "%s%s\n", pszIndent, szWork ); + fflush( f ); + pchLine += 16; + } + + return 0; +} + + + +//------------------------------------------------------------- +// endian stuff +section_header_t* ConvertSectionHeader( section_header_t* p) +{ + p->cbHeader = ntohl( p->cbHeader ); + p->cbSection = ntohl( p->cbSection ); + p->compId = ntohs( p->compId ); + // sctnVer is byte long + // subSect is byte long + return p; +} + + +//------------------------------------------------------------- +// endian stuff +marker_t* ConvertMarker( marker_t* p) +{ + p->offsetNext = ntohl( p->offsetNext ); + p->length = ntohl( p->length ); + return p; +} + + + +//------------------------------------------------------------- +// endian switch a uint64 +uint64_t ntohll( uint64_t i ) +{ + uint64_t hi; + uint64_t lo; + uint32_t * pword = reinterpret_cast<uint32_t*>(&i); + + hi = ntohl( *pword ); + lo = ntohl( *(pword+1) ); + + return (hi<<32)|lo; +} + + +//------------------------------------------------------------- +// endian stuff +errl_header_t* ConvertErrlHeader( errl_header_t* p ) +{ + p->cbytes = ntohl( p->cbytes ); + p->csections = ntohl( p->csections ); + p->reasonCode = ntohs( p->reasonCode ); + // p->modId is a byte + // p->sev is a byte + // p->eventType is a byte + // p->subSys is a byte + // p->srcType is a byte + p->termState = ntohl( p->termState ); + p->logId = ntohl( p->logId ); + p->user1 = ntohll( p->user1 ); + p->user2 = ntohll( p->user2 ); + p->CreationTime = ntohll( p->CreationTime ); + return p; +} + + + +//------------------------------------------------------------- +// endian stuff +storage_header_t * ConvertStorageHeader( storage_header_t * p ) +{ + p->cbStorage = ntohl( p->cbStorage ); + p->cInserted = ntohl( p->cInserted ); + p->offsetMarker = ntohl( p->offsetMarker ); + p->offsetStart = ntohl( p->offsetStart ); + return p; +} + + + +//----------------------------------------------------------------------- +// Given the binary image file name, return the errl storage part of the file. +// Caller must endian convert anything/everything in the output buffer. +// This function will allocate (malloc) the buffer and return a pointer to it. +// On fatal error, this function will print a diagnostic and end the process. +// Return how many bytes allocated for the buffer in io_cbBuffer. +char* ReadStorageBuffer(char* i_Image, uint32_t i_ulAddr, uint32_t &io_cbBuffer) +{ + int fd; + int rc; + int cb; + off_t offsetEnd; + char * l_pchBuffer = NULL; // pointer to return + storage_header_t header; + + // open the binary image of L3 RAM and read the errl log buffer + fd = open( i_Image, O_RDONLY ); + if ( fd == -1 ) + { + // write the error to stdout because more likely to be seen in Simics + fprintf(stdout, "Unable to open %s for reading.\n", i_Image); + fprintf(stdout, "Failed with errno %s\n", strerror(errno) ); + exit(2); + } + + offsetEnd = lseek( fd, 0, SEEK_END ); + if( i_ulAddr >= offsetEnd ) + { + fprintf( stdout, "Image file %s appears to be truncated. " + "Offset 0x%X exceeds size of image file.\n", + i_Image, i_ulAddr ); + exit(2); + } + + rc = lseek( fd, i_ulAddr, SEEK_SET ); + assert( -1 != rc ); + + + // Read just the header for the size of the buffer is + // stored in the header. + cb = read( fd, &header, sizeof( header )); + assert( -1 != cb ); + + // endian convert this copy of the header + ConvertStorageHeader( &header ); + + // io_cbBuffer is a count of bytes in storage + io_cbBuffer = header.cbStorage; + + if( ( i_ulAddr + io_cbBuffer ) > offsetEnd ) + { + fprintf( stdout, "Image file %s appears to be truncated. " + "Offset 0x%X exceeds size of image file.\n", + i_Image, i_ulAddr+io_cbBuffer ); + exit(2); + } + + // re-seek and re-read entire buffer this time. + rc = lseek( fd, i_ulAddr, SEEK_SET ); + assert( -1 != rc ); + + l_pchBuffer = static_cast<char *>(malloc( io_cbBuffer )); + assert( l_pchBuffer ); + + cb = read( fd, l_pchBuffer, io_cbBuffer ); + assert( -1 != cb ); + + close( fd ); +#if 0 + { + // Write the error log storage buffer to its own file. + // Offsets stored in the buffer are relative to the + // start of the buffer. When this file is saved in + // its own file, the same offsets are relative to the + // start of the file. This is convenient when debugging. + fd = open( "storagebuffer.bin", O_CREAT | O_RDWR, 0666 ); + if( -1 != fd ) + { + write( fd, l_pchBuffer, io_cbBuffer ); + close( fd ); + } + } +#endif + return l_pchBuffer; +} + + + + + + +//---------------------------------------------------------------------------- +// Open the given symbols file name, a text file, and find the storage address +// of error logs given by the symbol in pszSearch. +uint32_t FindSymbol( char * pszSymbolFile, char * pszSearch ) +{ + char * pszAddr = NULL; + char * pch; + char szWork[ 1024 ]; + uint32_t ulAddr = 0; + FILE * f; + + f = fopen( pszSymbolFile, "r" ); + if ( !f ) + { + fprintf(stdout, "Unable to open file %s for reading.\n", pszSymbolFile); + fprintf(stdout, "Failed with errno %s\n", strerror(errno) ); + exit(2); + } + + while( fgets( szWork, sizeof( szWork ), f )) + { + pch = strstr( szWork, pszSearch ); + if( pch ) + { + pszAddr = szWork + 2; + pch = strchr( pszAddr, ',' ); + assert( pch ); + *pch = 0; + break; + } + } + fclose(f); + + if( NULL == pszAddr ) + { + fprintf( stdout, "Cannot find %s in syms file.\n", pszSearch ); + exit(2); + } + + int c = sscanf( pszAddr, "%x", &ulAddr ); + if( 1 != c ) + { + fprintf( stdout, + "Error, expecting '%s' to convert to hexidecimal.\n", + pszAddr ); + exit(2); + } + return ulAddr; +} + + + +// -------------------------------------------------------------------------- +// Print a summary of the error log, no user-defined data nor detail. +// perrlog is already endian converted + +void PrintErrlSummary( errl_header_t * perrlog ) +{ + + // print headline + // comp id sev rc mod evt u1 u2 csec + printf( "%-7s %10s %4s %-6s %-4s %-4s %-18s %-18s %5s\n", + "comp", + "logid", + "sev", + "reason", + "mod", + "evnt", + "user1", + "user2", + "csect" ); + + + // comp id sev reason mod event user1 user2 csec + // code + printf( "%-7s %10d 0x%02x 0x%04x 0x%02x 0x%02x 0x%016llx 0x%016llx %5d\n", + FindComp( perrlog->reasonCode ), + perrlog->logId, + perrlog->sev, + perrlog->reasonCode, + perrlog->modId, + perrlog->eventType, + perrlog->user1, + perrlog->user2, + perrlog->csections // count of sections + ); +} + + +//--------------------------------------------------------------------------- +// +// +void PrintErrlDetail( errl_header_t * perrlog ) +{ + + // print the summary line + PrintErrlSummary( perrlog ); + + // print sections if any + if( perrlog->csections ) + { + int i; + section_header_t* psect; + + // first section header resides just past the errl_header_t + psect = reinterpret_cast<section_header_t*>(perrlog+1); + + // Endian convert it + ConvertSectionHeader( psect ); + + i = 0; + do + { + printf( + "\nSection %d: %-8s len=0x%04x, ver=0x%04x, subsection=0x%04x\n", + i, + FindComp( psect->compId ), + psect->cbSection, + psect->sctnVer, + psect->subSect + ); + + // The user-provided data resides just past the section header. + char * pUserData = reinterpret_cast<char*>(psect+1); + + // Print a hex dump (for now) of the user-provided data. + FormatBytes( stdout, pUserData, (int)psect->cbSection, 4 ); + + i++; + if( i >= perrlog->csections ) + { + // Leave the loop, and do not ConvertSectionHeader(). + break; + } + + // There's more; point to the next section. + int cb = psect->cbHeader + psect->cbSection; + char * p = (reinterpret_cast<char*>(psect)) + cb; + psect = reinterpret_cast<section_header_t*>(p); + + // Endian convert it. + ConvertSectionHeader( psect ); + } + while( 1 ); + } +} + + + + + + +//------------------------------------------------------------- +int main( int argc, char *argv[] ) +{ + char * pch; + char * pszImageFile = NULL; + char * pszSymbolFile = NULL; + char szWork[ 1024 ]; + unsigned char * puch; + char * pszSearch; + char * pszAddr = NULL; + char * pchBuffer; + uint32_t ulAddr; + char szDivider[ 256 ]; + uint32_t ulLogId = 0; + int c; + int cb; + int cbSearch; + int fd; + int i; + int k; + int item; + int fOK; + int rc; + int exitcode = 0; + uint32_t cbBuffer = 0; + off_t offset; + off_t offsetEnd; + int fVerbose = 0; + int fList = 1; + int fDetail = 0; + int fFound = 0; + + + // build a =========== divider for printfing + cb = 84; + assert( cb < sizeof( szDivider )); + memset( szDivider, '=', sizeof( szDivider )); + szDivider[ cb ] = 0; + + + // Examine args. + i = 1; + while ( i < argc ) + { + if( 0 == strcmp( "-v", argv[i] )) + { + fVerbose = 1; + } + else if( 0 == strcmp( "-d", argv[i] )) + { + i++; + if( i >= argc ) + { + fprintf( stdout, "Provide -d <logid>\n" ); + exit( 2 ); + } + int c = sscanf( argv[i], "%d", &ulLogId ); + if( c != 1 ) + { + fprintf( stdout, "Provide -d <decimal log ID>\n" ); + exit( 2 ); + } + fList = 0; + fDetail = 1; + } + else if( 0 == strcmp( "-l", argv[i] )) + { + fList = 1; + fDetail = 0; + } + else if( 0 == strcmp("?", argv[i]) || + 0 == strcmp("-?", argv[i]) || + 0 == strcmp("-h", argv[i]) || + 0 == strcmp("--help", argv[i])) + { + // help + halt( USAGE ); + } + else if( 0 == strcmp( "-", argv[i] )) + { + // unrecognized switch + halt( USAGE ); + } + else + { + // must be a file name + pch = strstr( argv[i], "syms" ); + if( pch ) + { + pszSymbolFile = strdup( argv[i] ); + } + else + { + pszImageFile = strdup( argv[i] ); + } + } + + i++; + } + + + // Check args. + if((!pszImageFile) || (!pszSymbolFile)) + { + halt( USAGE ); + } + + + + // Given the symbols file, locate the address/offset of the errl storage + // buffer. + ulAddr = FindSymbol( pszSymbolFile, "g_ErrlStorage" ); + if( fVerbose ) + { + printf( "Error log storage buffer offset: 0x%08x\n", ulAddr ); + } + + + // Given the image file, read the portion that contains the + // error logs. + pchBuffer = ReadStorageBuffer( pszImageFile, ulAddr, cbBuffer ); + assert( pchBuffer ); + assert( cbBuffer ); + if( fVerbose ) + { + printf("Errlog storage buffer size: %d (decimal) bytes\n", cbBuffer ); + } + + + // Convert the endianess of the storage header. + storage_header_t* pHeader = reinterpret_cast<storage_header_t*>(pchBuffer); + ConvertStorageHeader( pHeader ); + if( fVerbose ) + { + printf( "%d error logs were committed.\n", pHeader->cInserted ); + printf( "Start offset: 0x%08x\n", pHeader->offsetStart ); + printf( "Ending offset: 0x%08x\n", pHeader->offsetMarker ); + } + + +/** @brief Convert an offset to a marker_t pointer. */ +#define OFFSET2MARKER(offset) (reinterpret_cast<marker_t*>(pchBuffer+offset)) + + +/** @brief Convert a marker_t pointer to an offset within the buffer. */ +#define MARKER2OFFSET(p) ((reinterpret_cast<char*>(p))-pchBuffer) + + + + // Count how many error logs found + int logcount = 0; + + // Traverse the list of error logs in the buffer. At this time, the + // buffer does not wrap. It is a straight shot from start to finish. + // Follow the markers. The start-of-list marker: + marker_t* pMarker = ConvertMarker( OFFSET2MARKER(pHeader->offsetStart)); + + while( 1 ) + { + if( fVerbose ) + { + cb = printf( "Marker at 0x%06x: next 0x%06x, length 0x%06x\n", + MARKER2OFFSET(pMarker), + pMarker->offsetNext, + pMarker->length ); + } + + if( pMarker->offsetNext == 0 ) + { + // This is the list-ending marker. + printf( "%s\n", szDivider ); + break; + } + + assert( pMarker->length ); + + logcount++; + + // Flattened struct of an error log resides just past marker. + errl_header_t* perr = reinterpret_cast<errl_header_t*>(pMarker+1); + ConvertErrlHeader( perr ); + + if( fList ) + { + // Just list the error log headers. + printf( "%s\n", szDivider ); + PrintErrlSummary( perr ); + } + else if(( fDetail ) && ( perr->logId == ulLogId )) + { + // Print the detail for the one error log. + printf( "%s\n", szDivider ); + PrintErrlDetail( perr ); + fFound = 1; + } + + // next marker/error log + pMarker = ConvertMarker( OFFSET2MARKER(pMarker->offsetNext) ); + } + + + if( fVerbose ) + { + printf( "%d error logs found.\n", logcount ); + } + + if( fDetail && !fFound ) + { + printf( "Error log %d not found.\n", ulLogId ); + exitcode = 2; + } + return exitcode; +} + |

