llwindebug.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:24k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llwindebug.cpp
  3.  * @brief Windows debugging functions
  4.  *
  5.  * $LicenseInfo:firstyear=2004&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2004-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "llviewerprecompiledheaders.h"
  33. #include <tchar.h>
  34. #include <tlhelp32.h>
  35. #include "llwindebug.h"
  36. #include "llviewercontrol.h"
  37. #include "lldir.h"
  38. #include "llsd.h"
  39. #include "llsdserialize.h"
  40. #pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union
  41. #pragma warning(disable: 4100) //unreferenced formal parameter
  42. /*
  43. LLSD Block for Windows Dump Information
  44. <llsd>
  45.   <map>
  46.     <key>Platform</key>
  47.     <string></string>
  48.     <key>Process</key>
  49.     <string></string>
  50.     <key>Module</key>
  51.     <string></string>
  52.     <key>DateModified</key>
  53.     <string></string>
  54.     <key>ExceptionCode</key>
  55.     <string></string>
  56.     <key>ExceptionRead/WriteAddress</key>
  57.     <string></string>
  58.     <key>Instruction</key>
  59.     <string></string>
  60.     <key>Registers</key>
  61.     <map>
  62.       <!-- Continued for all registers -->
  63.       <key>EIP</key>
  64.       <string>...</string>
  65.       <!-- ... -->
  66.     </map>
  67.     <key>Call Stack</key>
  68.     <array>
  69.       <!-- One map per stack frame -->
  70.       <map>
  71. <key>ModuleName</key>
  72. <string></string>
  73. <key>ModuleBaseAddress</key>
  74. <string></string>
  75. <key>ModuleOffsetAddress</key>
  76. <string></string>
  77. <key>Parameters</key>
  78. <array>
  79.   <string></string>
  80. </array>
  81.       </map>
  82.       <!-- ... -->
  83.     </array>
  84.   </map>
  85. </llsd>
  86. */
  87. extern void (*gCrashCallback)(void);
  88. // based on dbghelp.h
  89. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
  90. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  91. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  92. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  93. );
  94. MINIDUMPWRITEDUMP f_mdwp = NULL;
  95. #undef UNICODE
  96. static LPTOP_LEVEL_EXCEPTION_FILTER gFilterFunc = NULL;
  97. HMODULE hDbgHelp;
  98. // Tool Help functions.
  99. typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
  100. typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
  101. typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
  102. CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
  103. MODULE32_FIRST Module32First_;
  104. MODULE32_NEST Module32Next_;
  105. #define DUMP_SIZE_MAX 8000 //max size of our dump
  106. #define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls
  107. #define NL L"rn" //new line
  108. typedef struct STACK
  109. {
  110. STACK * Ebp;
  111. PBYTE Ret_Addr;
  112. DWORD Param[0];
  113. } STACK, * PSTACK;
  114. BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr);
  115. void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, 
  116.    const CONTEXT* context_record, 
  117.    LLSD& info);
  118. void printError( CHAR* msg )
  119. {
  120.   DWORD eNum;
  121.   TCHAR sysMsg[256];
  122.   TCHAR* p;
  123.   eNum = GetLastError( );
  124.   FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  125.          NULL, eNum,
  126.          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  127.          sysMsg, 256, NULL );
  128.   // Trim the end of the line and terminate it with a null
  129.   p = sysMsg;
  130.   while( ( *p > 31 ) || ( *p == 9 ) )
  131.     ++p;
  132.   do { *p-- = 0; } while( ( p >= sysMsg ) &&
  133.                           ( ( *p == '.' ) || ( *p < 33 ) ) );
  134.   // Display the message
  135.   printf( "n  WARNING: %s failed with error %d (%s)", msg, eNum, sysMsg );
  136. }
  137. BOOL GetProcessThreadIDs(DWORD process_id, std::vector<DWORD>& thread_ids) 
  138.   HANDLE hThreadSnap = INVALID_HANDLE_VALUE; 
  139.   THREADENTRY32 te32; 
  140.  
  141.   // Take a snapshot of all running threads  
  142.   hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 
  143.   if( hThreadSnap == INVALID_HANDLE_VALUE ) 
  144.     return( FALSE ); 
  145.  
  146.   // Fill in the size of the structure before using it. 
  147.   te32.dwSize = sizeof(THREADENTRY32 ); 
  148.  
  149.   // Retrieve information about the first thread,
  150.   // and exit if unsuccessful
  151.   if( !Thread32First( hThreadSnap, &te32 ) ) 
  152.   {
  153.     printError( "Thread32First" );  // Show cause of failure
  154.     CloseHandle( hThreadSnap );     // Must clean up the snapshot object!
  155.     return( FALSE );
  156.   }
  157.   // Now walk the thread list of the system,
  158.   // and display information about each thread
  159.   // associated with the specified process
  160.   do 
  161.   { 
  162.     if( te32.th32OwnerProcessID == process_id )
  163.     {
  164.       thread_ids.push_back(te32.th32ThreadID); 
  165.     }
  166.   } while( Thread32Next(hThreadSnap, &te32 ) ); 
  167. //  Don't forget to clean up the snapshot object.
  168.   CloseHandle( hThreadSnap );
  169.   return( TRUE );
  170. }
  171. BOOL GetThreadCallStack(DWORD thread_id, LLSD& info)
  172. {
  173.     if(GetCurrentThreadId() == thread_id)
  174.     {
  175.         // Early exit for the current thread.
  176.         // Suspending the current thread would be a bad idea.
  177.         // Plus you can't retrieve a valid current thread context.
  178.         return false;
  179.     }
  180.     HANDLE thread_handle = INVALID_HANDLE_VALUE; 
  181.     thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id);
  182.     if(INVALID_HANDLE_VALUE == thread_handle)
  183.     {
  184.         return FALSE;
  185.     }
  186.     BOOL result = false;
  187.     if(-1 != SuspendThread(thread_handle))
  188.     {
  189.         CONTEXT context_struct;
  190.         context_struct.ContextFlags = CONTEXT_FULL;
  191.         if(GetThreadContext(thread_handle, &context_struct))
  192.         {
  193.             Get_Call_Stack(NULL, &context_struct, info);
  194.             result = true;
  195.         }
  196.         ResumeThread(thread_handle);
  197.     }
  198.     else
  199.     {
  200.         // Couldn't suspend thread.
  201.     }
  202.     CloseHandle(thread_handle);
  203.     return result;
  204. }
  205. //Windows Call Stack Construction idea from 
  206. //http://www.codeproject.com/tools/minidump.asp
  207. //****************************************************************************************
  208. BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr)
  209. //****************************************************************************************
  210. // Find module by Ret_Addr (address in the module).
  211. // Return Module_Name (full path) and Module_Addr (start address).
  212. // Return TRUE if found.
  213. {
  214. MODULEENTRY32 M = {sizeof(M)};
  215. HANDLE hSnapshot;
  216. bool found = false;
  217. if (CreateToolhelp32Snapshot_)
  218. {
  219. hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);
  220. if ((hSnapshot != INVALID_HANDLE_VALUE) &&
  221. Module32First_(hSnapshot, &M))
  222. {
  223. do
  224. {
  225. if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
  226. {
  227. lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
  228. Module_Addr = M.modBaseAddr;
  229. found = true;
  230. break;
  231. }
  232. } while (Module32Next_(hSnapshot, &M));
  233. }
  234. CloseHandle(hSnapshot);
  235. }
  236. return found;
  237. } //Get_Module_By_Ret_Addr
  238. bool has_valid_call_before(PDWORD cur_stack_loc)
  239. {
  240. PBYTE p_first_byte = (PBYTE)(*cur_stack_loc - 1);
  241. PBYTE p_second_byte = (PBYTE)(*cur_stack_loc -2);
  242. PBYTE p_fifth_byte = (PBYTE)(*cur_stack_loc - 5);
  243. PBYTE p_sixth_byte = (PBYTE)(*cur_stack_loc - 6);
  244. // make sure we can read it
  245. if(IsBadReadPtr(p_sixth_byte, 6 * sizeof(BYTE)))
  246. {
  247. return false;
  248. }
  249. // check for 9a + 4 bytes
  250. if(*p_fifth_byte == 0x9A)
  251. {
  252. return true;
  253. }
  254. // Check for E8 + 4 bytes and last byte is 00 or FF
  255. if(*p_fifth_byte == 0xE8 && (*p_first_byte == 0x00 || *p_first_byte == 0xFF))
  256. {
  257. return true;
  258. }
  259. // the other is six bytes
  260. if(*p_sixth_byte == 0xFF || *p_second_byte == 0xFF)
  261. {
  262. return true;
  263. }
  264. return false;
  265. }
  266. PBYTE get_valid_frame(PBYTE esp)
  267. {
  268. PDWORD cur_stack_loc = NULL;
  269. const int max_search = 400;
  270. WCHAR module_name[MAX_PATH];
  271. PBYTE module_addr = 0;
  272. // round to highest multiple of four
  273. esp = (esp + (4 - ((int)esp % 4)) % 4);
  274. // scroll through stack a few hundred places.
  275. for (cur_stack_loc = (PDWORD) esp; cur_stack_loc < (PDWORD)esp + max_search; cur_stack_loc += 1)
  276. {
  277. // if you can read the pointer,
  278. if (IsBadReadPtr(cur_stack_loc, sizeof(PDWORD)))
  279. {
  280. continue;
  281. }
  282. //  check if it's in a module
  283. if (!Get_Module_By_Ret_Addr((PBYTE)*cur_stack_loc, module_name, module_addr))
  284. {
  285. continue;
  286. }
  287. // check if the code before the instruction ptr is a call 
  288. if(!has_valid_call_before(cur_stack_loc))
  289. {
  290. continue;
  291. }
  292. // if these all pass, return that ebp, otherwise continue till we're dead
  293. return (PBYTE)(cur_stack_loc - 1);
  294. }
  295. return NULL;
  296. }
  297. bool shouldUseStackWalker(PSTACK Ebp, int max_depth)
  298. {
  299. WCHAR Module_Name[MAX_PATH];
  300. PBYTE Module_Addr = 0;
  301. int depth = 0;
  302. while (depth < max_depth) 
  303. {
  304. if (IsBadReadPtr(Ebp, sizeof(PSTACK)) || 
  305. IsBadReadPtr(Ebp->Ebp, sizeof(PSTACK)) ||
  306. Ebp->Ebp < Ebp ||
  307. Ebp->Ebp - Ebp > 0xFFFFFF ||
  308. IsBadCodePtr(FARPROC(Ebp->Ebp->Ret_Addr)) ||
  309. !Get_Module_By_Ret_Addr(Ebp->Ebp->Ret_Addr, Module_Name, Module_Addr))
  310. {
  311. return true;
  312. }
  313. depth++;
  314. Ebp = Ebp->Ebp;
  315. }
  316. return false;
  317. }
  318. //******************************************************************
  319. void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, 
  320.    const CONTEXT* context_record, 
  321.    LLSD& info)
  322. //******************************************************************
  323. // Fill Str with call stack info.
  324. // pException can be either GetExceptionInformation() or NULL.
  325. // If pException = NULL - get current call stack.
  326. {
  327. LPWSTR Module_Name = new WCHAR[MAX_PATH];
  328. PBYTE Module_Addr = 0;
  329. LLSD params;
  330. PBYTE Esp = NULL;
  331. LLSD tmp_info;
  332. bool fake_frame = false;
  333. bool ebp_used = false;
  334. const int HEURISTIC_MAX_WALK = 20;
  335. int heuristic_walk_i = 0;
  336. int Ret_Addr_I = 0;
  337. STACK Stack = {0, 0};
  338. PSTACK Ebp;
  339. if (exception_record && context_record) //fake frame for exception address
  340. {
  341. Stack.Ebp = (PSTACK)(context_record->Ebp);
  342. Stack.Ret_Addr = (PBYTE)exception_record->ExceptionAddress;
  343. Ebp = &Stack;
  344. Esp = (PBYTE) context_record->Esp;
  345. fake_frame = true;
  346. }
  347. else if(context_record)
  348. {
  349.         Ebp = (PSTACK)(context_record->Ebp);
  350. Esp = (PBYTE)(context_record->Esp);
  351. }
  352. else
  353. {
  354. Ebp = (PSTACK)&exception_record - 1; //frame addr of Get_Call_Stack()
  355. Esp = (PBYTE)&exception_record;
  356. // Skip frame of Get_Call_Stack().
  357. if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
  358. Ebp = Ebp->Ebp; //caller ebp
  359. }
  360. // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
  361. // Break trace on wrong stack frame.
  362. for (Ret_Addr_I = 0;
  363. heuristic_walk_i < HEURISTIC_MAX_WALK && 
  364. Ret_Addr_I < CALL_TRACE_MAX && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
  365. Ret_Addr_I++)
  366. {
  367. // If module with Ebp->Ret_Addr found.
  368. if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
  369. {
  370. // Save module's address and full path.
  371. tmp_info["CallStack"][Ret_Addr_I]["ModuleName"] = ll_convert_wide_to_string(Module_Name);
  372. tmp_info["CallStack"][Ret_Addr_I]["ModuleAddress"] = (int)Module_Addr;
  373. tmp_info["CallStack"][Ret_Addr_I]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr);
  374. // Save 5 params of the call. We don't know the real number of params.
  375. if (fake_frame && !Ret_Addr_I) //fake frame for exception address
  376. params[0] = "Exception Offset";
  377. else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
  378. {
  379. for(int j = 0; j < 5; ++j)
  380. {
  381. params[j] = (int)Ebp->Param[j];
  382. }
  383. }
  384. tmp_info["CallStack"][Ret_Addr_I]["Parameters"] = params;
  385. }
  386. tmp_info["CallStack"][Ret_Addr_I]["ReturnAddress"] = (int)Ebp->Ret_Addr;
  387. // get ready for next frame
  388. // Set ESP to just after return address.  Not the real esp, but just enough after the return address
  389. if(!fake_frame) {
  390. Esp = (PBYTE)Ebp + 8;
  391. else
  392. {
  393. fake_frame = false;
  394. }
  395. // is next ebp valid?
  396. // only run if we've never found a good ebp
  397. // and make sure the one after is valid as well
  398. if( !ebp_used && 
  399. shouldUseStackWalker(Ebp, 2))
  400. {
  401. heuristic_walk_i++;
  402. PBYTE new_ebp = get_valid_frame(Esp);
  403. if (new_ebp != NULL)
  404. {
  405. Ebp = (PSTACK)new_ebp;
  406. }
  407. }
  408. else
  409. {
  410. ebp_used = true;
  411. Ebp = Ebp->Ebp;
  412. }
  413. }
  414. /* TODO remove or turn this code back on to edit the stack after i see a few raw ones. -Palmer
  415. // Now go back through and edit out heuristic stacks that could very well be bogus.
  416. // Leave the top and the last 3 stack chosen by the heuristic, however.
  417. if(heuristic_walk_i > 2)
  418. {
  419. info["CallStack"][0] = tmp_info["CallStack"][0];
  420. std::string ttest = info["CallStack"][0]["ModuleName"];
  421. for(int cur_frame = 1; 
  422. (cur_frame + heuristic_walk_i - 2 < Ret_Addr_I); 
  423. ++cur_frame)
  424. {
  425. // edit out the middle heuristic found frames
  426. info["CallStack"][cur_frame] = tmp_info["CallStack"][cur_frame + heuristic_walk_i - 2];
  427. }
  428. }
  429. else
  430. {
  431. info = tmp_info;
  432. }
  433. */
  434. info = tmp_info;
  435. info["HeuristicWalkI"] = heuristic_walk_i;
  436. info["EbpUsed"] = ebp_used;
  437. } //Get_Call_Stack
  438. //***********************************
  439. void WINAPI Get_Version_Str(LLSD& info)
  440. //***********************************
  441. // Fill Str with Windows version.
  442. {
  443. OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later
  444. if (!GetVersionEx((POSVERSIONINFO)&V))
  445. {
  446. ZeroMemory(&V, sizeof(V));
  447. V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  448. GetVersionEx((POSVERSIONINFO)&V);
  449. }
  450. if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
  451. V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx
  452. info["Platform"] = llformat("Windows:  %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,...
  453. V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType);
  454. } //Get_Version_Str
  455. //*************************************************************
  456. LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
  457. //*************************************************************
  458. // Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
  459. {
  460. LLSD info;
  461. LPWSTR Str;
  462. int Str_Len;
  463. // int i;
  464. LPWSTR Module_Name = new WCHAR[MAX_PATH];
  465. PBYTE Module_Addr;
  466. HANDLE hFile;
  467. FILETIME Last_Write_Time;
  468. FILETIME Local_File_Time;
  469. SYSTEMTIME T;
  470. Str = new WCHAR[DUMP_SIZE_MAX];
  471. Str_Len = 0;
  472. if (!Str)
  473. return NULL;
  474. Get_Version_Str(info);
  475. GetModuleFileName(NULL, Str, MAX_PATH);
  476. info["Process"] = ll_convert_wide_to_string(Str);
  477. info["ThreadID"] = (S32)GetCurrentThreadId();
  478. // If exception occurred.
  479. if (pException)
  480. {
  481. EXCEPTION_RECORD & E = *pException->ExceptionRecord;
  482. CONTEXT & C = *pException->ContextRecord;
  483. // If module with E.ExceptionAddress found - save its path and date.
  484. if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
  485. {
  486. info["Module"] = ll_convert_wide_to_string(Module_Name);
  487. if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
  488. FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
  489. {
  490. if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
  491. {
  492. FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
  493. FileTimeToSystemTime(&Local_File_Time, &T);
  494. info["DateModified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear);
  495. }
  496. CloseHandle(hFile);
  497. }
  498. }
  499. else
  500. {
  501. info["ExceptionAddr"] = (int)E.ExceptionAddress;
  502. }
  503. info["ExceptionCode"] = (int)E.ExceptionCode;
  504. /*
  505. //TODO: Fix this
  506. if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
  507. {
  508. // Access violation type - Write/Read.
  509. LLSD exception_info;
  510. exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read";
  511. exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]);
  512. info["Exception Information"] = exception_info;
  513. }
  514. */
  515. // Save instruction that caused exception.
  516. /*
  517. std::string str;
  518. for (i = 0; i < 16; i++)
  519. str += llformat(" %02X", PBYTE(E.ExceptionAddress)[i]);
  520. info["Instruction"] = str;
  521. */
  522. LLSD registers;
  523. registers["EAX"] = (int)C.Eax;
  524. registers["EBX"] = (int)C.Ebx;
  525. registers["ECX"] = (int)C.Ecx;
  526. registers["EDX"] = (int)C.Edx;
  527. registers["ESI"] = (int)C.Esi;
  528. registers["EDI"] = (int)C.Edi;
  529. registers["ESP"] = (int)C.Esp;
  530. registers["EBP"] = (int)C.Ebp;
  531. registers["EIP"] = (int)C.Eip;
  532. registers["EFlags"] = (int)C.EFlags;
  533. info["Registers"] = registers;
  534. } //if (pException)
  535. // Save call stack info.
  536. Get_Call_Stack(pException->ExceptionRecord, pException->ContextRecord, info);
  537. return info;
  538. } //Get_Exception_Info
  539. #define UNICODE
  540. class LLMemoryReserve {
  541. public:
  542. LLMemoryReserve();
  543. ~LLMemoryReserve();
  544. void reserve();
  545. void release();
  546. protected:
  547. unsigned char *mReserve;
  548. static const size_t MEMORY_RESERVATION_SIZE;
  549. };
  550. LLMemoryReserve::LLMemoryReserve() :
  551. mReserve(NULL)
  552. {
  553. };
  554. LLMemoryReserve::~LLMemoryReserve()
  555. {
  556. release();
  557. }
  558. // I dunno - this just seemed like a pretty good value.
  559. const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024;
  560. void LLMemoryReserve::reserve()
  561. {
  562. if(NULL == mReserve)
  563. mReserve = new unsigned char[MEMORY_RESERVATION_SIZE];
  564. };
  565. void LLMemoryReserve::release()
  566. {
  567. delete [] mReserve;
  568. mReserve = NULL;
  569. };
  570. static LLMemoryReserve gEmergencyMemoryReserve;
  571. #ifndef _M_IX86
  572. #error "The following code only works for x86!"
  573. #endif
  574. LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
  575. LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
  576. {
  577. if(lpTopLevelExceptionFilter ==  gFilterFunc)
  578. return gFilterFunc;
  579. llinfos << "Someone tried to set the exception filter. Listing call stack modules" << llendl;
  580. LLSD cs_info;
  581. Get_Call_Stack(NULL, NULL, cs_info);
  582. if(cs_info.has("CallStack") && cs_info["CallStack"].isArray())
  583. {
  584. LLSD cs = cs_info["CallStack"];
  585. for(LLSD::array_iterator i = cs.beginArray(); 
  586. i != cs.endArray(); 
  587. ++i)
  588. {
  589. llinfos << "Module: " << (*i)["ModuleName"] << llendl;
  590. }
  591. }
  592. return gFilterFunc;
  593. }
  594. BOOL PreventSetUnhandledExceptionFilter()
  595. {
  596. HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
  597. if (hKernel32 == NULL) 
  598. return FALSE;
  599. void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
  600. if(pOrgEntry == NULL) 
  601. return FALSE;
  602. unsigned char newJump[ 100 ];
  603. DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
  604. dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
  605. void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
  606. DWORD dwNewEntryAddr = (DWORD) pNewFunc;
  607. DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
  608. newJump[ 0 ] = 0xE9;  // JMP absolute
  609. memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
  610. SIZE_T bytesWritten;
  611. BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
  612. pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
  613. return bRet;
  614. }
  615. // static
  616. void  LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
  617. {
  618. static bool s_first_run = true;
  619. // Load the dbghelp dll now, instead of waiting for the crash.
  620. // Less potential for stack mangling
  621. if (s_first_run)
  622. {
  623. // First, try loading from the directory that the app resides in.
  624. std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir());
  625. HMODULE hDll = NULL;
  626. hDll = LoadLibraryA(local_dll_name.c_str());
  627. if (!hDll)
  628. {
  629. hDll = LoadLibrary(L"dbghelp.dll");
  630. }
  631. if (!hDll)
  632. {
  633. LL_WARNS("AppInit") << "Couldn't find dbghelp.dll!" << LL_ENDL;
  634. }
  635. else
  636. {
  637. f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
  638. if (!f_mdwp)
  639. {
  640. FreeLibrary(hDll);
  641. hDll = NULL;
  642. }
  643. }
  644. gEmergencyMemoryReserve.reserve();
  645. s_first_run = false;
  646. }
  647. // Try to get Tool Help library functions.
  648. HMODULE hKernel32;
  649. hKernel32 = GetModuleHandle(_T("KERNEL32"));
  650. CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
  651. Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW");
  652. Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW");
  653.     LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
  654. prev_filter = SetUnhandledExceptionFilter(filter_func);
  655. // *REMOVE:Mani
  656. //PreventSetUnhandledExceptionFilter();
  657. if(prev_filter != gFilterFunc)
  658. {
  659. LL_WARNS("AppInit") 
  660. << "Replacing unknown exception (" << (void *)prev_filter << ") with (" << (void *)filter_func << ") !" << LL_ENDL;
  661. }
  662. gFilterFunc = filter_func;
  663. }
  664. bool LLWinDebug::checkExceptionHandler()
  665. {
  666. bool ok = true;
  667. LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
  668. prev_filter = SetUnhandledExceptionFilter(gFilterFunc);
  669. if (prev_filter != gFilterFunc)
  670. {
  671. LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with " << prev_filter << "!" << LL_ENDL;
  672. ok = false;
  673. }
  674. if (prev_filter == NULL)
  675. {
  676. ok = FALSE;
  677. if (gFilterFunc == NULL)
  678. {
  679. LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
  680. }
  681. else
  682. {
  683. LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with NULL!" << LL_ENDL;
  684. }
  685. }
  686. return ok;
  687. }
  688. void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename)
  689. {
  690. if(f_mdwp == NULL || gDirUtilp == NULL) 
  691. {
  692. return;
  693. //write_debug("No way to generate a minidump, no MiniDumpWriteDump function!n");
  694. }
  695. else
  696. {
  697. std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename);
  698. HANDLE hFile = CreateFileA(dump_path.c_str(),
  699. GENERIC_WRITE,
  700. FILE_SHARE_WRITE,
  701. NULL,
  702. CREATE_ALWAYS,
  703. FILE_ATTRIBUTE_NORMAL,
  704. NULL);
  705. if (hFile != INVALID_HANDLE_VALUE)
  706. {
  707. // Write the dump, ignoring the return value
  708. f_mdwp(GetCurrentProcess(),
  709. GetCurrentProcessId(),
  710. hFile,
  711. type,
  712. ExInfop,
  713. NULL,
  714. NULL);
  715. CloseHandle(hFile);
  716. }
  717. }
  718. }
  719. // static
  720. void LLWinDebug::generateCrashStacks(struct _EXCEPTION_POINTERS *exception_infop)
  721. {
  722. // *NOTE:Mani - This method is no longer the exception handler.
  723. // Its called from viewer_windows_exception_handler() and other places.
  724. // 
  725. // Let go of a bunch of reserved memory to give library calls etc
  726. // a chance to execute normally in the case that we ran out of
  727. // memory.
  728. //
  729. LLSD info;
  730. std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
  731. "SecondLifeException");
  732. std::string log_path = dump_path + ".log";
  733. if (exception_infop)
  734. {
  735. // Since there is exception info... Release the hounds.
  736. gEmergencyMemoryReserve.release();
  737. if(gSavedSettings.getControl("SaveMinidump").notNull() && gSavedSettings.getBOOL("SaveMinidump"))
  738. {
  739. _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
  740. ExInfo.ThreadId = ::GetCurrentThreadId();
  741. ExInfo.ExceptionPointers = exception_infop;
  742. ExInfo.ClientPointers = NULL;
  743. writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
  744. writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
  745. }
  746. info = Get_Exception_Info(exception_infop);
  747. }
  748. LLSD threads;
  749.     std::vector<DWORD> thread_ids;
  750.     GetProcessThreadIDs(GetCurrentProcessId(), thread_ids);
  751.     for(std::vector<DWORD>::iterator th_itr = thread_ids.begin(); 
  752.                 th_itr != thread_ids.end();
  753.                 ++th_itr)
  754.     {
  755.         LLSD thread_info;
  756.         if(*th_itr != GetCurrentThreadId())
  757.         {
  758.             GetThreadCallStack(*th_itr, thread_info);
  759.         }
  760.         if(thread_info)
  761.         {
  762.             threads[llformat("ID %d", *th_itr)] = thread_info;
  763.         }
  764.     }
  765.     info["Threads"] = threads;
  766. llofstream out_file(log_path);
  767. LLSDSerialize::toPrettyXML(info, out_file);
  768. out_file.close();
  769. }
  770. void LLWinDebug::clearCrashStacks()
  771. {
  772. LLSD info;
  773. std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeException.log");
  774. LLFile::remove(dump_path);
  775. }