Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

539 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include <windows.h>
  3. #include "mdmpRipper.h"
  4. #include <vgui/IVGui.h>
  5. #include "vgui_controls/MessageMap.h"
  6. #include "vgui_controls/MenuBar.h"
  7. #include "vgui_controls/Menu.h"
  8. #include "vgui_controls/MessageBox.h"
  9. #include "tier1/KeyValues.h"
  10. #include "vgui/ISurface.h"
  11. #include <vgui/ILocalize.h>
  12. #include "vgui_controls/Frame.h"
  13. #include "CMDModulePanel.h"
  14. #include "vgui_controls/ListPanel.h"
  15. #include <vgui_controls/RichText.h>
  16. #include "KeyValues.h"
  17. #include "vgui/ISystem.h"
  18. #include "vgui_controls/FileOpenDialog.h"
  19. #include "isqlwrapper.h"
  20. #include "CMDRipperMain.h"
  21. extern ISQLWrapper *g_pSqlWrapper;
  22. using namespace vgui;
  23. CMDModulePanel::CMDModulePanel( vgui::Panel *pParent, const char *pName ) :
  24. BaseClass( pParent, pName, true )
  25. {
  26. m_pTokenList = new ListPanel(this, "ModuleList");
  27. m_pTokenList->AddColumnHeader(0, "name", "Module Name", 600, 0);
  28. m_pTokenList->AddColumnHeader(1, "version", "Version", 100, 0);
  29. m_pTokenList->AddColumnHeader(2, "count", "Count", 86, 0);
  30. m_pTokenList->AddActionSignalTarget( this );
  31. m_pAnalyzeText = new RichText(this, "AnalyzeText");
  32. m_pAnalyzeText->SetVerticalScrollbar(true);
  33. LoadControlSettings( "MDModulePanel.res" );
  34. m_pAnalyzeText->InsertString("Initializing...\n");
  35. InitializeDebugEngine();
  36. LoadKnownModules();
  37. m_hThread = NULL;
  38. // SetTitleBarVisible( false );
  39. // SetSizeable( false );
  40. //SETUP_PANEL( this );
  41. }
  42. CMDModulePanel::~CMDModulePanel( void )
  43. {
  44. ReleaseDebugEngine( );
  45. }
  46. void CMDModulePanel::OnKeyCodeTyped( KeyCode code )
  47. {
  48. switch ( code )
  49. {
  50. case KEY_G:
  51. UpdateKnownDB( "GOOD" );
  52. break;
  53. case KEY_B:
  54. UpdateKnownDB( "BAD" );
  55. break;
  56. case KEY_U:
  57. UpdateKnownDB( "UNKNOWN" );
  58. break;
  59. case KEY_F:
  60. ModuleLookUp();
  61. break;
  62. }
  63. }
  64. void CMDModulePanel::OnCommand( const char *pCommand )
  65. {
  66. if ( !Q_strcmp( pCommand, "Close" ) )
  67. {
  68. //we want to close
  69. Close();
  70. }
  71. if ( !Q_strcmp( pCommand, "ModuleLookUp" ) )
  72. {
  73. ModuleLookUp();
  74. }
  75. if ( !Q_strcmp( pCommand, "SetGood" ) )
  76. {
  77. UpdateKnownDB( "GOOD" );
  78. }
  79. if ( !Q_strcmp( pCommand, "SetBad" ) )
  80. {
  81. UpdateKnownDB( "BAD" );
  82. }
  83. if ( !Q_strcmp( pCommand, "SetUnknown" ) )
  84. {
  85. UpdateKnownDB( "UNKNOWN" );
  86. }
  87. }
  88. void CMDModulePanel::Close()
  89. {
  90. if ( this )
  91. {
  92. m_pTokenList->DeleteAllItems();
  93. m_MiniDumpList.RemoveAll();
  94. m_knownModuleList.RemoveAll();
  95. m_pAnalyzeText->SetText("");
  96. SetVisible( false );
  97. KeyValues *kv = new KeyValues( "Refresh" );
  98. this->PostActionSignal( kv );
  99. }
  100. }
  101. void CMDModulePanel::Create( CUtlVector<CMiniDumpObject *> *pMiniDump )
  102. {
  103. LoadKnownModules();
  104. for ( int i = 0; i < pMiniDump->Count(); i++ )
  105. {
  106. pMiniDump->Element(i)->PopulateListPanel( m_pTokenList, true );
  107. }
  108. }
  109. void CMDModulePanel::Create( const char *filename )
  110. {
  111. if ( g_pFullFileSystem->FileExists( filename ) )
  112. {
  113. LoadKnownModules();
  114. CMiniDumpObject *newMDObj = new CMiniDumpObject( filename, &m_knownModuleList );
  115. m_MiniDumpList.AddToTail( newMDObj );
  116. newMDObj->PopulateListPanel( m_pTokenList, false );
  117. AnalyzeDumpFile( filename );
  118. }
  119. }
  120. void CMDModulePanel::ModuleLookUp()
  121. {
  122. int selectedIndex = m_pTokenList->GetSelectedItem( 0 );
  123. void *kv = m_pTokenList->GetItem( selectedIndex );
  124. if ( kv )
  125. {
  126. const char *val = ((KeyValues *)kv)->GetString( "name", "" );
  127. if ( val )
  128. {
  129. const char *moduleName = strrchr( val, '\\' ) + 1;
  130. char google[1024] = "";
  131. sprintf( google, "http://www.google.com/search?hl=en&q=%s", moduleName);
  132. KeyValues *kvPost = new KeyValues( "ModuleLookUp", "url", google );
  133. this->PostActionSignal( kvPost );
  134. }
  135. }
  136. }
  137. void SeparateVersion( const char *version, char *v1buf, char *v2buf, char *v3buf, char *v4buf )
  138. {
  139. const char *endV1 = strchr( version, '.' )+1;
  140. const char *endV2 = strchr( endV1+1, '.' )+1;
  141. const char *endV3 = strchr( endV2+1, '.' )+1;
  142. _mbsnbcpy( (unsigned char *)v1buf, (const unsigned char*)version, endV1 - version );
  143. v1buf[endV1 - version - 1] = 0;
  144. _mbsnbcpy( (unsigned char *)v2buf, (const unsigned char*)endV1, endV2 - endV1 );
  145. v2buf[endV2 - endV1 - 1] = 0;
  146. _mbsnbcpy( (unsigned char *)v3buf, (const unsigned char*)endV2, endV3 - endV2 );
  147. v3buf[endV3 - endV2 - 1] = 0;
  148. strcpy( v4buf, endV3 );
  149. }
  150. void SetKeyValueColor( char *type, KeyValues *kv, bool knownVersion )
  151. {
  152. int colorValue = 255;
  153. if( !knownVersion )
  154. colorValue = 155;
  155. if ( !Q_strcmp( "GOOD", type ) )
  156. {
  157. ((KeyValues *)kv)->SetColor( "cellcolor", Color(0,colorValue,0,255));
  158. }
  159. else if ( !Q_strcmp( "BAD", type ) )
  160. {
  161. ((KeyValues *)kv)->SetColor( "cellcolor", Color(colorValue,0,0,255));
  162. }
  163. else
  164. {
  165. ((KeyValues *)kv)->SetColor( "cellcolor", Color(255,255,0,255));
  166. }
  167. }
  168. void CMDModulePanel::UpdateKnownDB( char *type )
  169. {
  170. int selectedIndex = m_pTokenList->GetSelectedItem( 0 );
  171. void *kv = m_pTokenList->GetItem( selectedIndex );
  172. char v1buf[10];
  173. char v2buf[10];
  174. char v3buf[10];
  175. char v4buf[10];
  176. char name[65];
  177. char keybuf[10];
  178. if ( kv )
  179. {
  180. SetKeyValueColor( type, (KeyValues *)kv, true );
  181. int key = ((KeyValues *)kv)->GetInt( "key" );
  182. itoa( key, keybuf, 10 );
  183. strcpy( name, strrchr(((KeyValues *)kv)->GetString( "name" ), '\\')+1);
  184. SeparateVersion( ((KeyValues *)kv)->GetString("version"), v1buf, v2buf, v3buf, v4buf );
  185. if ( key == 0 )
  186. {
  187. //as far as we know, this is a non-existant module.
  188. if ( !Q_strcmp( type, "UNKNOWN" ) )
  189. {
  190. return;
  191. }
  192. else
  193. {
  194. char query[1024];
  195. sprintf( query, "select * from knownmodules where name = \"%s\" and version1 = %s and version2 = %s and version3 = %s and version4 = %s;",
  196. name, v1buf, v2buf, v3buf, v4buf );
  197. IResultSet *results = g_pSqlWrapper->PResultSetQuery( query ); // do the query
  198. if ( !results )
  199. {
  200. return;
  201. }
  202. int numResults = results->GetCSQLRow();
  203. if ( numResults > 0 )
  204. {
  205. //there is an entry... get our module list up to date with this entry
  206. const ISQLRow *row = results->PSQLRowNextResult();
  207. Assert( row != NULL );
  208. int realKey = row->NData(0);
  209. const char *realType = row->PchData(6);
  210. g_pSqlWrapper->FreeResult();
  211. ((KeyValues *)kv)->SetInt( "key", realKey );
  212. if ( !Q_strcmp( realType, type ) )
  213. {
  214. //this user was out of sync with the database. It doesn't actually need updating.
  215. return;
  216. }
  217. else
  218. {
  219. char update[1024];
  220. sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey);
  221. g_pSqlWrapper->BInsert( update );
  222. }
  223. }
  224. else
  225. {
  226. g_pSqlWrapper->FreeResult();
  227. //it isn't in there. Let's add it.
  228. char update[1024];
  229. sprintf( update, "insert into knownmodules set name = \"%s\", version1 = %s, version2 = %s, version3 = %s, version4 = %s, type = \"%s\";",
  230. name, v1buf, v2buf, v3buf, v4buf, type);
  231. g_pSqlWrapper->BInsert( update );
  232. results = g_pSqlWrapper->PResultSetQuery( query ); // do the query
  233. int numResults = results->GetCSQLRow();
  234. if ( numResults > 0 )
  235. {
  236. const ISQLRow *row = results->PSQLRowNextResult();
  237. Assert( row != NULL );
  238. int realKey = row->NData(0);
  239. ((KeyValues *)kv)->SetInt( "key", realKey );
  240. }
  241. g_pSqlWrapper->FreeResult();
  242. }
  243. }
  244. }
  245. else
  246. {
  247. char query[1024];
  248. sprintf( query, "select * from knownmodules where id = %i;",
  249. key);
  250. IResultSet *results = g_pSqlWrapper->PResultSetQuery( query ); // do the query
  251. int numResults = results->GetCSQLRow();
  252. if ( numResults > 0 )
  253. {
  254. //there is an entry... update it with the new info...
  255. const ISQLRow *row = results->PSQLRowNextResult();
  256. Assert( row != NULL );
  257. Assert( numResults == 1 );
  258. Assert( !Q_stricmp( name, row->PchData(1) ) && atoi( v1buf ) == row->NData(2) && atoi( v2buf ) == row->NData(3) &&
  259. atoi( v3buf ) == row->NData(4) && atoi( v4buf ) == row->NData(5) );
  260. int realKey = row->NData(0);
  261. const char *realType = row->PchData(6);
  262. g_pSqlWrapper->FreeResult();
  263. if ( !Q_strcmp( realType, type ) )
  264. {
  265. //we don't need to update... it is already updated already
  266. return;
  267. }
  268. char update[1024];
  269. sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey);
  270. g_pSqlWrapper->BInsert( update );
  271. }
  272. else
  273. {
  274. //the module entry was mis-keyed. First, check for an existing entry of this module.
  275. char query[1024];
  276. sprintf( query, "select * from knownmodules where name = \"%s\" and version1 = %s and version2 = %s and version3 = %s and version4 = %s;",
  277. name, v1buf, v2buf, v3buf, v4buf );
  278. IResultSet *results = g_pSqlWrapper->PResultSetQuery( query ); // do the query
  279. int numResults = results->GetCSQLRow();
  280. if ( numResults > 0 )
  281. {
  282. //there is an existing entry. Update its type and update the key for this keyvalue;
  283. const ISQLRow *row = results->PSQLRowNextResult();
  284. int realKey = row->NData(0);
  285. ((KeyValues *)kv)->SetInt( "key", realKey );
  286. g_pSqlWrapper->FreeResult();
  287. char update[1024];
  288. sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey);
  289. g_pSqlWrapper->BInsert( update );
  290. }
  291. else
  292. {
  293. g_pSqlWrapper->FreeResult();
  294. //no exisiting entry. Insert it.
  295. char update[1024];
  296. sprintf( update, "insert into knownmodules set name = \"%s\", version1 = %s, version2 = %s, version3 = %s, version4 = %s, type = \"%s\";",
  297. name, v1buf, v2buf, v3buf, v4buf, type);
  298. g_pSqlWrapper->BInsert( update );
  299. results = g_pSqlWrapper->PResultSetQuery( query ); // do the query
  300. int numResults = results->GetCSQLRow();
  301. if ( numResults > 0 )
  302. {
  303. const ISQLRow *row = results->PSQLRowNextResult();
  304. Assert( row != NULL );
  305. int realKey = row->NData(0);
  306. ((KeyValues *)kv)->SetInt( "key", realKey );
  307. }
  308. g_pSqlWrapper->FreeResult();
  309. }
  310. }
  311. }
  312. }
  313. }
  314. void CMDModulePanel::OnCompare( KeyValues *data )
  315. {
  316. LoadKnownModules();
  317. CUtlVector<HANDLE> *pMiniDumpHandles = (CUtlVector<HANDLE> *)(void *)data->GetInt( "handlePointer" );
  318. DWORD error;
  319. int returnValue = 0;
  320. for( int i = 0; i < pMiniDumpHandles->Count(); i++ )
  321. {
  322. m_MiniDumpList.AddToTail( new CMiniDumpObject( pMiniDumpHandles->Element( i ), &m_knownModuleList ) );
  323. returnValue = CloseHandle( pMiniDumpHandles->Element( i ) );
  324. error = GetLastError();
  325. }
  326. Create( &m_MiniDumpList );
  327. SetVisible( true );
  328. MoveToFront();
  329. pMiniDumpHandles->RemoveAll();
  330. system("rmdir c:\\minidumptool /s/q");
  331. }
  332. void CMDModulePanel::OnDbgOutput( int iMask, const char *pszDebugText)
  333. {
  334. if ( m_pAnalyzeText && pszDebugText )
  335. {
  336. m_pAnalyzeText->InsertString( pszDebugText );
  337. }
  338. }
  339. DWORD WINAPI CMDModulePanel::StaticAnalyzeThread( LPVOID lParam )
  340. {
  341. CMDModulePanel *pClass = (CMDModulePanel *)lParam;
  342. if ( pClass )
  343. {
  344. pClass->AnalyzeThread( );
  345. }
  346. return ( 0 );
  347. }
  348. void CMDModulePanel::LoadKnownModules()
  349. {
  350. if ( m_knownModuleList.Count() > 0 )
  351. return;
  352. char rgchQueryBuf[ 1024 ] = "SELECT * from knownmodules;";
  353. IResultSet *results = g_pSqlWrapper->PResultSetQuery( rgchQueryBuf );
  354. if ( !results )
  355. {
  356. ivgui()->DPrintf( "LoadKnownModules() results are NULL" );
  357. VGUIMessageBox( GetParent(), "Error", "Unable to retrieve known modules from database" );
  358. return;
  359. }
  360. for ( int i = 0; i < results->GetCSQLRow(); i++ )
  361. {
  362. module newModule;
  363. const ISQLRow *row = results->PSQLRowNextResult();
  364. Assert( row != NULL );
  365. newModule.key = row->NData(0);
  366. strcpy( newModule.name, row->PchData(1));
  367. newModule.versionInfo.v1 = row->NData(2);
  368. newModule.versionInfo.v2 = row->NData(3);
  369. newModule.versionInfo.v3 = row->NData(4);
  370. newModule.versionInfo.v4 = row->NData(5);
  371. if ( !Q_strcmp( row->PchData(6), "GOOD" ) )
  372. {
  373. newModule.myType = GOOD;
  374. }
  375. else if ( !Q_strcmp( row->PchData(6), "BAD" ) )
  376. {
  377. newModule.myType = BAD;
  378. }
  379. else
  380. {
  381. newModule.myType = UNKNOWN;
  382. }
  383. m_knownModuleList.AddToTail( newModule );
  384. }
  385. g_pSqlWrapper->FreeResult();
  386. }
  387. void CMDModulePanel::InitializeDebugEngine( void )
  388. {
  389. // Start things off by getting an initial interface from
  390. // the engine. This can be any engine interface but is
  391. // generally IDebugClient as the client interface is
  392. // where sessions are started.
  393. if ( S_OK == DebugCreate( __uuidof ( IDebugClient ),
  394. (void**)&m_pDbgClient ) )
  395. {
  396. m_pDbgClient->QueryInterface( __uuidof ( IDebugControl ),
  397. ( void** )&m_pDbgControl );
  398. m_pDbgClient->QueryInterface( __uuidof ( IDebugSymbols2 ),
  399. ( void** )&m_pDbgSymbols );
  400. // Set out Panel to receive the debug outputs from the engine
  401. m_cDbgOutput.SetOutputPanel( GetVPanel() );
  402. // Install output callbacks so we get any output that the
  403. // later calls produce.
  404. m_pDbgClient->SetOutputCallbacks(&m_cDbgOutput);
  405. if ( m_pDbgSymbols )
  406. {
  407. // Make sure we have a symbol path to use
  408. char szSymbolSrv[ 512 ] = { 0 };
  409. ExpandEnvironmentStrings( "%_NT_SYMBOL_PATH%", szSymbolSrv, sizeof (szSymbolSrv) );
  410. if ( !Q_stricmp( "%_NT_SYMBOL_PATH%", szSymbolSrv ) )
  411. {
  412. ivgui()->DPrintf( "Setting symbol server" );
  413. Q_strcpy( szSymbolSrv, "SRV*c:\\localsymbols*\\\\perforce\\symbols*http://msdl.microsoft.com/download/symbols" );
  414. m_pDbgSymbols->SetSymbolPath( szSymbolSrv );
  415. }
  416. m_pDbgSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
  417. }
  418. }
  419. }
  420. void CMDModulePanel::ReleaseDebugEngine( void )
  421. {
  422. // Clean up any resources.
  423. if ( m_pDbgSymbols != NULL )
  424. {
  425. m_pDbgSymbols->Release( );
  426. }
  427. if ( m_pDbgControl != NULL )
  428. {
  429. m_pDbgControl->Release( );
  430. }
  431. if ( m_pDbgClient != NULL )
  432. {
  433. // We don't want to see any output from the shutdown.
  434. m_pDbgClient->SetOutputCallbacks( NULL );
  435. m_pDbgClient->EndSession( DEBUG_END_PASSIVE );
  436. m_pDbgClient->Release( );
  437. }
  438. }
  439. void CMDModulePanel::AnalyzeDumpFile( const char *pszDumpFile )
  440. {
  441. if (m_pDbgClient && m_pDbgControl)
  442. {
  443. char szBuf[ 256 ] = { 0 };
  444. Q_snprintf( szBuf, sizeof ( szBuf ), "About to open [%s].\n", pszDumpFile );
  445. m_pAnalyzeText->InsertString( szBuf );
  446. // Everything's set up so open the dump file.
  447. m_pDbgClient->OpenDumpFile(pszDumpFile);
  448. // Finish initialization by waiting for the event that
  449. // caused the dump. This will return immediately as the
  450. // dump file is considered to be at its event.
  451. m_pDbgControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);
  452. DWORD dwThreadId = 0;
  453. m_hThread = CreateThread( NULL, 0, StaticAnalyzeThread, (LPVOID)this, 0, &dwThreadId );
  454. }
  455. }
  456. DWORD CMDModulePanel::AnalyzeThread( void )
  457. {
  458. if ( m_pDbgControl )
  459. {
  460. // Tell the debug engine to analyze the current dump file
  461. m_pDbgControl->Execute( DEBUG_OUTCTL_THIS_CLIENT,
  462. "!analyze -v",
  463. DEBUG_EXECUTE_DEFAULT);
  464. }
  465. CloseHandle( m_hThread );
  466. m_pAnalyzeText->InsertString( "Finished analyzing minidump file.\n" );
  467. return ( 0 );
  468. }