/*=========================================================================== FILE: DataParser.cpp DESCRIPTION: Implementation of sParsedField and cDataParser PUBLIC CLASSES AND METHODS: sParsedField Structure to represent a single parsed field (field ID, offset, size, value, name, etc.) cDataParser Class to parse a buffer into bit/byte specified fields accordinging to a database description, uses cProtocolEntityNav to navigate the DB definition Copyright (c) 2011, Code Aurora Forum. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Code Aurora Forum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ===========================================================================*/ //--------------------------------------------------------------------------- // Include Files //--------------------------------------------------------------------------- #include "StdAfx.h" #include "DataParser.h" #include "CoreDatabase.h" #include "DB2Utilities.h" #include //--------------------------------------------------------------------------- // Definitions //--------------------------------------------------------------------------- /*=========================================================================*/ // sParsedField Methods /*=========================================================================*/ /*=========================================================================== METHOD: sParsedField (Public Method) DESCRIPTION: Construct a parsed field by setting the values and extracting the data according to the definition PARAMETERS: db [ I ] - Database to use field [ I ] - Field description (from database) name [ I ] - Desired field name bp [I/O] - Bit parser to use bGenStrings [ I ] - Generate field value strings? NOTE: 'bGenStrings' does not apply to fields that are string types? RETURN VALUE: None ===========================================================================*/ sParsedField::sParsedField( const cCoreDatabase & db, const sDB2Field * pField, const std::string & name, cBitParser & bp, bool bGenStrings ) : mField(), mOffset( bp.GetNumBitsParsed() ), mValueString( "" ), mName( name ), mbValid( false ) { // Clear value memset( (PVOID)&mValue, 0, (SIZE_T)sizeof( mValue ) ); // Assume failure bool bOK = false; if (pField == 0) { return; } mField = *pField; char tempValueString[128]; memset( &tempValueString[0], 0, 128 ); // What type is this field? switch (mField.mType) { case eDB2_FIELD_STD: { // Standard field, what kind? eDB2StdFieldType ft = (eDB2StdFieldType)mField.mTypeVal; switch (ft) { // Field is a boolean (0/1, false/true)/8-bit unsigned integer case eDB2_FIELD_STDTYPE_BOOL: case eDB2_FIELD_STDTYPE_UINT8: { // We store as a UCHAR UCHAR val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Constrain boolean values? if (ft == eDB2_FIELD_STDTYPE_BOOL && val > 1) { val = 1; } // Success! mValue.mU8 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%02X", (UINT)mValue.mU8 ); } else { snprintf( &tempValueString[0], 0, "%u", (UINT)mValue.mU8 ); } mValueString = &tempValueString[0]; } } } break; // Field is 8-bit signed integer case eDB2_FIELD_STDTYPE_INT8: { // We store as a CHAR CHAR val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mS8 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%02X", (UINT)mValue.mU8 ); } else { snprintf( &tempValueString[0], 0, "%d", (INT)mValue.mS8 ); } mValueString = &tempValueString[0]; } } } break; // Field is 16-bit signed integer case eDB2_FIELD_STDTYPE_INT16: { // We store as a SHORT SHORT val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mS16 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%04hX", mValue.mU16 ); } else { snprintf( &tempValueString[0], 0, "%hd", mValue.mS16 ); } mValueString = &tempValueString[0]; } } } break; // Field is 16-bit unsigned integer case eDB2_FIELD_STDTYPE_UINT16: { // We store as a USHORT USHORT val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mU16 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%04hX", mValue.mU16 ); } else { snprintf( &tempValueString[0], 0, "%hu", mValue.mU16 ); } mValueString = &tempValueString[0]; } } } break; // Field is 32-bit signed integer case eDB2_FIELD_STDTYPE_INT32: { // We store as a LONG LONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mS32 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%08lX", mValue.mU32 ); } else { snprintf( &tempValueString[0], 0, "%ld", mValue.mS32 ); } mValueString = &tempValueString[0]; } } } break; // Field is 32-bit unsigned integer case eDB2_FIELD_STDTYPE_UINT32: { // We store as a ULONG ULONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mU32 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%08lX", mValue.mU32 ); } else { snprintf( &tempValueString[0], 0, "%lu", mValue.mU32 ); } mValueString = &tempValueString[0]; } } } break; // Field is 64-bit signed integer case eDB2_FIELD_STDTYPE_INT64: { // We store as a LONGLONG LONGLONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mS64 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%016llX", mValue.mU64 ); } else { snprintf( &tempValueString[0], 0, "%lld", mValue.mS64 ); } mValueString = &tempValueString[0]; } } } break; // Field is 64-bit unsigned integer case eDB2_FIELD_STDTYPE_UINT64: { // We store as a ULONGLONG ULONGLONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mU64 = val; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%016llX", mValue.mU64 ); } else { snprintf( &tempValueString[0], 0, "%llu", mValue.mU64 ); } mValueString = &tempValueString[0]; } } } break; // ANSI/UNICODE fixed length string case eDB2_FIELD_STDTYPE_STRING_A: case eDB2_FIELD_STDTYPE_STRING_U: { // Compute the number of characters ULONG numChars = mField.mSize / BITS_PER_BYTE; // Parse out the string bOK = ParseString( numChars, bp ); } break; // ANSI NULL terminated string case eDB2_FIELD_STDTYPE_STRING_ANT: { // Figure out the length of the string ULONG numChars = 0; // Temporarily assume success bOK = true; ULONG tmpOffset = bp.GetNumBitsParsed(); CHAR val = 1; while (val != 0) { DWORD rc = bp.Get( BITS_PER_BYTE, val ); if (rc == NO_ERROR) { numChars++; } else { val = 0; } } // Now actually parse/load the string if (bOK == true) { bp.SetOffset( tmpOffset ); bOK = ParseString( numChars, bp ); } } break; // UNICODE NULL terminated string case eDB2_FIELD_STDTYPE_STRING_UNT: { // Figure out the length of the string ULONG numChars = 0; // Temporarily assume success bOK = true; ULONG tmpOffset = bp.GetNumBitsParsed(); USHORT val = 1; while (val != 0) { DWORD rc = bp.Get( BITS_PER_BYTE, val ); if (rc == NO_ERROR) { numChars++; } else { val = 0; } } // Now actually parse/load the string if (bOK == true) { bp.SetOffset( tmpOffset ); bOK = ParseString( numChars, bp ); } } break; case eDB2_FIELD_STDTYPE_STRING_U8: case eDB2_FIELD_STDTYPE_STRING_U8NT: // Unsupported in the Linux adaptation bOK = false; break; // Field is 32-bit floating point value case eDB2_FIELD_STDTYPE_FLOAT32: { // We store as a ULONG ULONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { FLOAT * pFloat = (FLOAT *)&val; // Success! mValue.mFP32 = *pFloat; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%04lX", mValue.mU32 ); } else { snprintf( &tempValueString[0], 0, "%f", mValue.mFP32 ); } mValueString = &tempValueString[0]; } } } break; // Field is 64-bit floating point value case eDB2_FIELD_STDTYPE_FLOAT64: { // We store as a ULONGLONG ULONGLONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { DOUBLE * pFloat = (DOUBLE *)&val; // Success! mValue.mFP64 = *pFloat; bOK = true; if (bGenStrings == true) { if (mField.mbHex == true) { snprintf( &tempValueString[0], 0, "0x%08llX", mValue.mU64 ); } else { snprintf( &tempValueString[0], 0, "%f", mValue.mFP64 ); } mValueString = &tempValueString[0]; } } } break; } } break; // Unsigend enum value case eDB2_FIELD_ENUM_UNSIGNED: { // We store as a ULONG ULONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mU32 = val; bOK = true; // Grab the enum ID ULONG id = pField->mTypeVal; // Map to a string? if (bGenStrings == true) { mValueString = db.MapEnumToString( id, (int)mValue.mU32, true, mField.mbHex ); } } } break; // Signed enum value case eDB2_FIELD_ENUM_SIGNED: { // We store as a LONG LONG val; DWORD rc = bp.Get( mField.mSize, val ); if (rc == NO_ERROR) { // Success! mValue.mS32 = val; bOK = true; // Grab the enum ID ULONG id = pField->mTypeVal; // Map to a string? if (bGenStrings == true) { mValueString = db.MapEnumToString( id, (int)mValue.mS32, true, mField.mbHex ); } } } break; } mbValid = bOK; } /*=========================================================================== METHOD: ParseString (Public Method) DESCRIPTION: Convert the field value to a string PARAMETERS: numChars [ I ] - Number of characters to parse/load bp [I/O] - Bit parser to use RETURN VALUE: bool ===========================================================================*/ bool sParsedField::ParseString( ULONG numChars, cBitParser & bp ) { // Validate size (including null char) if (MAX_SHARED_BUFFER_SIZE < numChars + 1) { return false; } // Assume success bool bRC = true; // Store current offset so we can update field length ULONG curOffset = bp.GetNumBitsParsed(); // Load each byte of the string individually BYTE buf[MAX_SHARED_BUFFER_SIZE]; for (ULONG c = 0; c < numChars; c++) { BYTE val = 0; DWORD rc = bp.Get( BITS_PER_BYTE, val ); if (rc == NO_ERROR) { buf[c] = val; } else { bRC = false; break; } } if (bRC == true) { // Write zeros to the rest of the buffer ULONG size = numChars; ULONG end = numChars + 1; for (ULONG current = size; current < end; current++) { buf[current] = 0; } mValueString = (LPCSTR)&buf[0]; mValue.mpAStr = (LPCSTR)mValueString.c_str(); // Update field size mField.mSize = bp.GetNumBitsParsed() - curOffset; } return bRC; } /*=========================================================================*/ // cParsedFieldNavigator Methods /*=========================================================================*/ /*=========================================================================== METHOD: GetFieldIndex (Public Method) DESCRIPTION: Get index of the (first) field that matches the given field ID PARAMETERS: fieldID [ I ] - Field ID to look for bLoop [ I ] - Loop around end of field list? RETURN VALUE: ULONG - Index of the field (0xFFFFFFFF upon failure) ===========================================================================*/ ULONG cParsedFieldNavigator::GetFieldIndex( ULONG fieldID, bool bLoop ) const { ULONG id = ULONG_MAX; ULONG count = (ULONG)mFields.size(); // Start from last field ID? ULONG fp = 0; ULONG fi = 0; if (mLastIDIndex < count) { fi = mLastIDIndex; } else if (mLastIDIndex != ULONG_MAX && bLoop == false) { // Beyond end of fields with no looping mLastIDIndex = id; return id; } for (fp = 0; fp < count; fp++) { if (mFields[fi].mField.mID == fieldID) { id = fi; break; } fi++; if (fi == count) { if (bLoop == true) { fi = 0; } else { break; } } } // Update last ID accordingly (0xFFFFFFFF upon failure), and return mLastIDIndex = id; if (mLastIDIndex != ULONG_MAX) { mLastIDIndex++; if (mLastIDIndex == count) { mLastIDIndex = 0; } } return id; } /*=========================================================================*/ // cDataParser Methods /*=========================================================================*/ /*=========================================================================== METHOD: cDataParser (Public Method) DESCRIPTION: Constructor (protocol buffer, entity key, and payload) PARAMETERS: db [ I ] - Database to use buffer [ I ] - The protocol buffer being parsed key [ I ] - Protocol entity key pData [ I ] - Payload from above protocol buffer dataLen [ I ] - Size of above payload RETURN VALUE: None ===========================================================================*/ cDataParser::cDataParser( const cCoreDatabase & db, const sProtocolBuffer & buffer, const std::vector & key, const BYTE * pData, ULONG dataLen ) : cProtocolEntityNav( db ), mBuffer( buffer.GetSharedBuffer() ), mbFieldStrings( true ), mbParsed( false ) { // We must have a valid protocol buffer if (mBuffer.IsValid() == false) { return; } // We need something to parse if (pData == 0 || dataLen == 0) { return; } // Key has to be proper if (key.size() < 1) { return; } // Key needs to match protocol eProtocolType pt = (eProtocolType)mBuffer.GetType(); eDB2EntityType et = (eDB2EntityType)key[0]; if (pt == ePROTOCOL_DIAG_RX || pt == ePROTOCOL_DIAG_TX) { if (IsDiagEntityType( et ) == false) { return; } } else if (IsQMIProtocol( pt ) == true) { if (IsQMIEntityType( et ) == false) { return; } } // Pass data to the bit parser mKey = key; mBitsy.SetData( pData, dataLen * BITS_PER_BYTE ); } /*=========================================================================== METHOD: ~cDataParser (Public Method) DESCRIPTION: Destructor RETURN VALUE: None ===========================================================================*/ cDataParser::~cDataParser() { // Ask bit parser to release data mBitsy.ReleaseData(); // Empty fields mFields.clear(); } /*=========================================================================== METHOD: Parse (Public Method) DESCRIPTION: Parse the data to a list of fields/summary text PARAMETERS: bFieldStrings [ I ] - Generate string representations of field values? bFieldNames [ I ] - Generate (partial) field names? RETURN VALUE: bool ===========================================================================*/ bool cDataParser::Parse( bool bFieldStrings, bool bFieldNames ) { // Store parsing options mbFieldStrings = bFieldStrings; mbFieldNames = bFieldNames; if (mbParsed == false) { // Allocate space for 1024 fields up front in order to increase // performance when parsing and accessing parsed fields mFields.reserve( 1024 ); // Process (parse) the protocol entity mbParsed = ProcessEntity( mKey ); } return mbParsed; } /*=========================================================================== METHOD: GetLastValue (Internal Method) DESCRIPTION: Working from the back of the current field list find and return the value for the specified field ID as a LONGLONG (field type must be able to fit in a LONGLONG for a value to be returned) PARAMETERS: fieldID [ I ] - Field ID we are looking for val [ O ] - The value RETURN VALUE: bool ===========================================================================*/ bool cDataParser::GetLastValue( ULONG fieldID, LONGLONG & val ) { // Assume failure bool bRC = false; // Use field value tracking information std::map >::iterator pTF; pTF = mTrackedFields.find( fieldID ); if (pTF != mTrackedFields.end() && pTF->second.first == true) { val = pTF->second.second; bRC = true; } return bRC; } /*=========================================================================== METHOD: ContinueNavigation (Internal Method) DESCRIPTION: Continue navigation now that entity has been set? RETURN VALUE: bool ===========================================================================*/ bool cDataParser::ContinueNavigation() { // Proceed to parse? bool bParse = true; // Is there actually something to process? if (mBitsy.GetNumBitsLeft() == 0) { bParse = false; } return bParse; } /*=========================================================================== METHOD: ProcessField (Internal Method) DESCRIPTION: Process the given field by parsing the value PARAMETERS: pField [ I ] - The field being processed fieldName [ I ] - Field name (partial) arrayIndex [ I ] - Not used RETURN VALUE: bool ===========================================================================*/ bool cDataParser::ProcessField( const sDB2Field * pField, const std::string & fieldName, LONGLONG /* arrayIndex */ ) { // Assume failure bool bRC = false; if (pField == 0) { return bRC; } // We must have a name sParsedField theField( mDB, pField, fieldName, mBitsy, mbFieldStrings ); // Did that result in a valid field? if (theField.IsValid() == true) { // Add field mFields.push_back( theField ); bRC = true; // Are we tracking the value of this field? std::map >::iterator pTF; pTF = mTrackedFields.find( pField->mID ); if (pTF != mTrackedFields.end()) { std::pair & entry = pTF->second; // What type is this field? switch (pField->mType) { case eDB2_FIELD_STD: { // Standard field, what kind? eDB2StdFieldType ft = (eDB2StdFieldType)pField->mTypeVal; switch (ft) { // Field is a boolean (0/1, false/true)/8-bit unsigned case eDB2_FIELD_STDTYPE_BOOL: case eDB2_FIELD_STDTYPE_UINT8: { // Treat as UCHAR entry.second = (LONGLONG)theField.mValue.mU8; entry.first = true; } break; // Field is 8-bit signed integer case eDB2_FIELD_STDTYPE_INT8: { // Treat as CHAR entry.second = (LONGLONG)theField.mValue.mS8; entry.first = true; } break; // Field is 16-bit signed integer case eDB2_FIELD_STDTYPE_INT16: { // Treat as SHORT entry.second = (LONGLONG)theField.mValue.mS16; entry.first = true; } break; // Field is 16-bit unsigned integer case eDB2_FIELD_STDTYPE_UINT16: { // Treat as USHORT entry.second = (LONGLONG)theField.mValue.mU16; entry.first = true; } break; // Field is 32-bit signed integer case eDB2_FIELD_STDTYPE_INT32: { // Treat as LONG entry.second = (LONGLONG)theField.mValue.mS32; entry.first = true; } break; // Field is 32-bit unsigned integer case eDB2_FIELD_STDTYPE_UINT32: { // Treat as ULONG entry.second = (LONGLONG)theField.mValue.mU32; entry.first = true; } break; // Field is 64-bit signed integer case eDB2_FIELD_STDTYPE_INT64: { // Treat as LONGLONG entry.second = (LONGLONG)theField.mValue.mS64; entry.first = true; } break; // Field is 64-bit unsigned integer case eDB2_FIELD_STDTYPE_UINT64: { // Treat as ULONGLONG if (theField.mValue.mU64 <= LLONG_MAX) { entry.second = (LONGLONG)theField.mValue.mU64; entry.first = true; } } break; } } break; case eDB2_FIELD_ENUM_UNSIGNED: { // Treat as ULONG entry.second = (LONGLONG)theField.mValue.mU32; entry.first = true; } break; case eDB2_FIELD_ENUM_SIGNED: { // Treat as LONG entry.second = (LONGLONG)theField.mValue.mS32; entry.first = true; } break; } } } return bRC; } /*=========================================================================== METHOD: HandleSpecialCases (Internal Method) DESCRIPTION: Handle special case processing for summary text generation NOTE: This should only be added to as a last resort PARAMETERS: args [I/O] - Current argument list/updated 'special' argument list fs [I/O] - Current format specifier/updated 'special' format specifier RETURN VALUE: None ===========================================================================*/ void cDataParser::HandleSpecialCases( std::string & args, std::string & fs ) { std::vector key = mEntity.GetKey(); if (key.size() == 2 && key[0] == (ULONG)eDB2_ET_DIAG_EVENT) { ULONG id = key[1]; mBitsy.SetOffset( 0 ); ULONG lenInBits = mBitsy.GetNumBitsLeft(); switch (id) { case 276: if (lenInBits == 16) { // Old style idle handoff event, remap summary args = "idle_handoff"; fs = "idle_handoff=%u"; } break; case 277: if (lenInBits == 16) { // Old style access handoff event, remap summary args = "ms_access_handoff"; fs = "ms_access_handoff=%u"; } break; case 278: if (lenInBits == 16) { // Old style access probe handoff event, remap summary args = "ms_access_probe_handoff"; fs = "ms_access_probe_handoff=%u"; } break; case 639: if (lenInBits == 16) { // Old style access entry handoff event, remap summary args = "ms_access_handoff"; fs = "ms_access_handoff=%u"; } break; } } }