//========= Copyright Valve Corporation, All rights reserved. ============// #include #include "mdmpRipper.h" #include #include "vgui_controls/MessageMap.h" #include "vgui_controls/MenuBar.h" #include "vgui_controls/Menu.h" #include "vgui_controls/MessageBox.h" #include "tier1/KeyValues.h" #include "vgui/ISurface.h" #include #include "vgui_controls/Frame.h" #include "CMDModulePanel.h" #include "vgui_controls/ListPanel.h" #include #include "KeyValues.h" #include "vgui/ISystem.h" #include "vgui_controls/FileOpenDialog.h" #include "isqlwrapper.h" #include "CMDRipperMain.h" extern ISQLWrapper *g_pSqlWrapper; using namespace vgui; CMDModulePanel::CMDModulePanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName, true ) { m_pTokenList = new ListPanel(this, "ModuleList"); m_pTokenList->AddColumnHeader(0, "name", "Module Name", 600, 0); m_pTokenList->AddColumnHeader(1, "version", "Version", 100, 0); m_pTokenList->AddColumnHeader(2, "count", "Count", 86, 0); m_pTokenList->AddActionSignalTarget( this ); m_pAnalyzeText = new RichText(this, "AnalyzeText"); m_pAnalyzeText->SetVerticalScrollbar(true); LoadControlSettings( "MDModulePanel.res" ); m_pAnalyzeText->InsertString("Initializing...\n"); InitializeDebugEngine(); LoadKnownModules(); m_hThread = NULL; // SetTitleBarVisible( false ); // SetSizeable( false ); //SETUP_PANEL( this ); } CMDModulePanel::~CMDModulePanel( void ) { ReleaseDebugEngine( ); } void CMDModulePanel::OnKeyCodeTyped( KeyCode code ) { switch ( code ) { case KEY_G: UpdateKnownDB( "GOOD" ); break; case KEY_B: UpdateKnownDB( "BAD" ); break; case KEY_U: UpdateKnownDB( "UNKNOWN" ); break; case KEY_F: ModuleLookUp(); break; } } void CMDModulePanel::OnCommand( const char *pCommand ) { if ( !Q_strcmp( pCommand, "Close" ) ) { //we want to close Close(); } if ( !Q_strcmp( pCommand, "ModuleLookUp" ) ) { ModuleLookUp(); } if ( !Q_strcmp( pCommand, "SetGood" ) ) { UpdateKnownDB( "GOOD" ); } if ( !Q_strcmp( pCommand, "SetBad" ) ) { UpdateKnownDB( "BAD" ); } if ( !Q_strcmp( pCommand, "SetUnknown" ) ) { UpdateKnownDB( "UNKNOWN" ); } } void CMDModulePanel::Close() { if ( this ) { m_pTokenList->DeleteAllItems(); m_MiniDumpList.RemoveAll(); m_knownModuleList.RemoveAll(); m_pAnalyzeText->SetText(""); SetVisible( false ); KeyValues *kv = new KeyValues( "Refresh" ); this->PostActionSignal( kv ); } } void CMDModulePanel::Create( CUtlVector *pMiniDump ) { LoadKnownModules(); for ( int i = 0; i < pMiniDump->Count(); i++ ) { pMiniDump->Element(i)->PopulateListPanel( m_pTokenList, true ); } } void CMDModulePanel::Create( const char *filename ) { if ( g_pFullFileSystem->FileExists( filename ) ) { LoadKnownModules(); CMiniDumpObject *newMDObj = new CMiniDumpObject( filename, &m_knownModuleList ); m_MiniDumpList.AddToTail( newMDObj ); newMDObj->PopulateListPanel( m_pTokenList, false ); AnalyzeDumpFile( filename ); } } void CMDModulePanel::ModuleLookUp() { int selectedIndex = m_pTokenList->GetSelectedItem( 0 ); void *kv = m_pTokenList->GetItem( selectedIndex ); if ( kv ) { const char *val = ((KeyValues *)kv)->GetString( "name", "" ); if ( val ) { const char *moduleName = strrchr( val, '\\' ) + 1; char google[1024] = ""; sprintf( google, "http://www.google.com/search?hl=en&q=%s", moduleName); KeyValues *kvPost = new KeyValues( "ModuleLookUp", "url", google ); this->PostActionSignal( kvPost ); } } } void SeparateVersion( const char *version, char *v1buf, char *v2buf, char *v3buf, char *v4buf ) { const char *endV1 = strchr( version, '.' )+1; const char *endV2 = strchr( endV1+1, '.' )+1; const char *endV3 = strchr( endV2+1, '.' )+1; _mbsnbcpy( (unsigned char *)v1buf, (const unsigned char*)version, endV1 - version ); v1buf[endV1 - version - 1] = 0; _mbsnbcpy( (unsigned char *)v2buf, (const unsigned char*)endV1, endV2 - endV1 ); v2buf[endV2 - endV1 - 1] = 0; _mbsnbcpy( (unsigned char *)v3buf, (const unsigned char*)endV2, endV3 - endV2 ); v3buf[endV3 - endV2 - 1] = 0; strcpy( v4buf, endV3 ); } void SetKeyValueColor( char *type, KeyValues *kv, bool knownVersion ) { int colorValue = 255; if( !knownVersion ) colorValue = 155; if ( !Q_strcmp( "GOOD", type ) ) { ((KeyValues *)kv)->SetColor( "cellcolor", Color(0,colorValue,0,255)); } else if ( !Q_strcmp( "BAD", type ) ) { ((KeyValues *)kv)->SetColor( "cellcolor", Color(colorValue,0,0,255)); } else { ((KeyValues *)kv)->SetColor( "cellcolor", Color(255,255,0,255)); } } void CMDModulePanel::UpdateKnownDB( char *type ) { int selectedIndex = m_pTokenList->GetSelectedItem( 0 ); void *kv = m_pTokenList->GetItem( selectedIndex ); char v1buf[10]; char v2buf[10]; char v3buf[10]; char v4buf[10]; char name[65]; char keybuf[10]; if ( kv ) { SetKeyValueColor( type, (KeyValues *)kv, true ); int key = ((KeyValues *)kv)->GetInt( "key" ); itoa( key, keybuf, 10 ); strcpy( name, strrchr(((KeyValues *)kv)->GetString( "name" ), '\\')+1); SeparateVersion( ((KeyValues *)kv)->GetString("version"), v1buf, v2buf, v3buf, v4buf ); if ( key == 0 ) { //as far as we know, this is a non-existant module. if ( !Q_strcmp( type, "UNKNOWN" ) ) { return; } else { char query[1024]; sprintf( query, "select * from knownmodules where name = \"%s\" and version1 = %s and version2 = %s and version3 = %s and version4 = %s;", name, v1buf, v2buf, v3buf, v4buf ); IResultSet *results = g_pSqlWrapper->PResultSetQuery( query ); // do the query if ( !results ) { return; } int numResults = results->GetCSQLRow(); if ( numResults > 0 ) { //there is an entry... get our module list up to date with this entry const ISQLRow *row = results->PSQLRowNextResult(); Assert( row != NULL ); int realKey = row->NData(0); const char *realType = row->PchData(6); g_pSqlWrapper->FreeResult(); ((KeyValues *)kv)->SetInt( "key", realKey ); if ( !Q_strcmp( realType, type ) ) { //this user was out of sync with the database. It doesn't actually need updating. return; } else { char update[1024]; sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey); g_pSqlWrapper->BInsert( update ); } } else { g_pSqlWrapper->FreeResult(); //it isn't in there. Let's add it. char update[1024]; sprintf( update, "insert into knownmodules set name = \"%s\", version1 = %s, version2 = %s, version3 = %s, version4 = %s, type = \"%s\";", name, v1buf, v2buf, v3buf, v4buf, type); g_pSqlWrapper->BInsert( update ); results = g_pSqlWrapper->PResultSetQuery( query ); // do the query int numResults = results->GetCSQLRow(); if ( numResults > 0 ) { const ISQLRow *row = results->PSQLRowNextResult(); Assert( row != NULL ); int realKey = row->NData(0); ((KeyValues *)kv)->SetInt( "key", realKey ); } g_pSqlWrapper->FreeResult(); } } } else { char query[1024]; sprintf( query, "select * from knownmodules where id = %i;", key); IResultSet *results = g_pSqlWrapper->PResultSetQuery( query ); // do the query int numResults = results->GetCSQLRow(); if ( numResults > 0 ) { //there is an entry... update it with the new info... const ISQLRow *row = results->PSQLRowNextResult(); Assert( row != NULL ); Assert( numResults == 1 ); Assert( !Q_stricmp( name, row->PchData(1) ) && atoi( v1buf ) == row->NData(2) && atoi( v2buf ) == row->NData(3) && atoi( v3buf ) == row->NData(4) && atoi( v4buf ) == row->NData(5) ); int realKey = row->NData(0); const char *realType = row->PchData(6); g_pSqlWrapper->FreeResult(); if ( !Q_strcmp( realType, type ) ) { //we don't need to update... it is already updated already return; } char update[1024]; sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey); g_pSqlWrapper->BInsert( update ); } else { //the module entry was mis-keyed. First, check for an existing entry of this module. char query[1024]; sprintf( query, "select * from knownmodules where name = \"%s\" and version1 = %s and version2 = %s and version3 = %s and version4 = %s;", name, v1buf, v2buf, v3buf, v4buf ); IResultSet *results = g_pSqlWrapper->PResultSetQuery( query ); // do the query int numResults = results->GetCSQLRow(); if ( numResults > 0 ) { //there is an existing entry. Update its type and update the key for this keyvalue; const ISQLRow *row = results->PSQLRowNextResult(); int realKey = row->NData(0); ((KeyValues *)kv)->SetInt( "key", realKey ); g_pSqlWrapper->FreeResult(); char update[1024]; sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey); g_pSqlWrapper->BInsert( update ); } else { g_pSqlWrapper->FreeResult(); //no exisiting entry. Insert it. char update[1024]; sprintf( update, "insert into knownmodules set name = \"%s\", version1 = %s, version2 = %s, version3 = %s, version4 = %s, type = \"%s\";", name, v1buf, v2buf, v3buf, v4buf, type); g_pSqlWrapper->BInsert( update ); results = g_pSqlWrapper->PResultSetQuery( query ); // do the query int numResults = results->GetCSQLRow(); if ( numResults > 0 ) { const ISQLRow *row = results->PSQLRowNextResult(); Assert( row != NULL ); int realKey = row->NData(0); ((KeyValues *)kv)->SetInt( "key", realKey ); } g_pSqlWrapper->FreeResult(); } } } } } void CMDModulePanel::OnCompare( KeyValues *data ) { LoadKnownModules(); CUtlVector *pMiniDumpHandles = (CUtlVector *)(void *)data->GetInt( "handlePointer" ); DWORD error; int returnValue = 0; for( int i = 0; i < pMiniDumpHandles->Count(); i++ ) { m_MiniDumpList.AddToTail( new CMiniDumpObject( pMiniDumpHandles->Element( i ), &m_knownModuleList ) ); returnValue = CloseHandle( pMiniDumpHandles->Element( i ) ); error = GetLastError(); } Create( &m_MiniDumpList ); SetVisible( true ); MoveToFront(); pMiniDumpHandles->RemoveAll(); system("rmdir c:\\minidumptool /s/q"); } void CMDModulePanel::OnDbgOutput( int iMask, const char *pszDebugText) { if ( m_pAnalyzeText && pszDebugText ) { m_pAnalyzeText->InsertString( pszDebugText ); } } DWORD WINAPI CMDModulePanel::StaticAnalyzeThread( LPVOID lParam ) { CMDModulePanel *pClass = (CMDModulePanel *)lParam; if ( pClass ) { pClass->AnalyzeThread( ); } return ( 0 ); } void CMDModulePanel::LoadKnownModules() { if ( m_knownModuleList.Count() > 0 ) return; char rgchQueryBuf[ 1024 ] = "SELECT * from knownmodules;"; IResultSet *results = g_pSqlWrapper->PResultSetQuery( rgchQueryBuf ); if ( !results ) { ivgui()->DPrintf( "LoadKnownModules() results are NULL" ); VGUIMessageBox( GetParent(), "Error", "Unable to retrieve known modules from database" ); return; } for ( int i = 0; i < results->GetCSQLRow(); i++ ) { module newModule; const ISQLRow *row = results->PSQLRowNextResult(); Assert( row != NULL ); newModule.key = row->NData(0); strcpy( newModule.name, row->PchData(1)); newModule.versionInfo.v1 = row->NData(2); newModule.versionInfo.v2 = row->NData(3); newModule.versionInfo.v3 = row->NData(4); newModule.versionInfo.v4 = row->NData(5); if ( !Q_strcmp( row->PchData(6), "GOOD" ) ) { newModule.myType = GOOD; } else if ( !Q_strcmp( row->PchData(6), "BAD" ) ) { newModule.myType = BAD; } else { newModule.myType = UNKNOWN; } m_knownModuleList.AddToTail( newModule ); } g_pSqlWrapper->FreeResult(); } void CMDModulePanel::InitializeDebugEngine( void ) { // Start things off by getting an initial interface from // the engine. This can be any engine interface but is // generally IDebugClient as the client interface is // where sessions are started. if ( S_OK == DebugCreate( __uuidof ( IDebugClient ), (void**)&m_pDbgClient ) ) { m_pDbgClient->QueryInterface( __uuidof ( IDebugControl ), ( void** )&m_pDbgControl ); m_pDbgClient->QueryInterface( __uuidof ( IDebugSymbols2 ), ( void** )&m_pDbgSymbols ); // Set out Panel to receive the debug outputs from the engine m_cDbgOutput.SetOutputPanel( GetVPanel() ); // Install output callbacks so we get any output that the // later calls produce. m_pDbgClient->SetOutputCallbacks(&m_cDbgOutput); if ( m_pDbgSymbols ) { // Make sure we have a symbol path to use char szSymbolSrv[ 512 ] = { 0 }; ExpandEnvironmentStrings( "%_NT_SYMBOL_PATH%", szSymbolSrv, sizeof (szSymbolSrv) ); if ( !Q_stricmp( "%_NT_SYMBOL_PATH%", szSymbolSrv ) ) { ivgui()->DPrintf( "Setting symbol server" ); Q_strcpy( szSymbolSrv, "SRV*c:\\localsymbols*\\\\perforce\\symbols*http://msdl.microsoft.com/download/symbols" ); m_pDbgSymbols->SetSymbolPath( szSymbolSrv ); } m_pDbgSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES); } } } void CMDModulePanel::ReleaseDebugEngine( void ) { // Clean up any resources. if ( m_pDbgSymbols != NULL ) { m_pDbgSymbols->Release( ); } if ( m_pDbgControl != NULL ) { m_pDbgControl->Release( ); } if ( m_pDbgClient != NULL ) { // We don't want to see any output from the shutdown. m_pDbgClient->SetOutputCallbacks( NULL ); m_pDbgClient->EndSession( DEBUG_END_PASSIVE ); m_pDbgClient->Release( ); } } void CMDModulePanel::AnalyzeDumpFile( const char *pszDumpFile ) { if (m_pDbgClient && m_pDbgControl) { char szBuf[ 256 ] = { 0 }; Q_snprintf( szBuf, sizeof ( szBuf ), "About to open [%s].\n", pszDumpFile ); m_pAnalyzeText->InsertString( szBuf ); // Everything's set up so open the dump file. m_pDbgClient->OpenDumpFile(pszDumpFile); // Finish initialization by waiting for the event that // caused the dump. This will return immediately as the // dump file is considered to be at its event. m_pDbgControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE); DWORD dwThreadId = 0; m_hThread = CreateThread( NULL, 0, StaticAnalyzeThread, (LPVOID)this, 0, &dwThreadId ); } } DWORD CMDModulePanel::AnalyzeThread( void ) { if ( m_pDbgControl ) { // Tell the debug engine to analyze the current dump file m_pDbgControl->Execute( DEBUG_OUTCTL_THIS_CLIENT, "!analyze -v", DEBUG_EXECUTE_DEFAULT); } CloseHandle( m_hThread ); m_pAnalyzeText->InsertString( "Finished analyzing minidump file.\n" ); return ( 0 ); }