00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #ifdef LL_WINDOWS
00035
00036 #include <tchar.h>
00037 #include <tlhelp32.h>
00038 #include "llwindebug.h"
00039 #include "llviewercontrol.h"
00040 #include "lldir.h"
00041 #include "llsd.h"
00042 #include "llsdserialize.h"
00043
00044 #pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union
00045 #pragma warning(disable: 4100) //unreferenced formal parameter
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 extern void (*gCrashCallback)(void);
00096
00097
00098 typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
00099 CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
00100 CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
00101 CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
00102 );
00103
00104 MINIDUMPWRITEDUMP f_mdwp = NULL;
00105
00106 #undef UNICODE
00107
00108 static LPTOP_LEVEL_EXCEPTION_FILTER gFilterFunc = NULL;
00109
00110 HMODULE hDbgHelp;
00111
00112
00113 typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
00114 typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
00115 typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
00116
00117 CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
00118 MODULE32_FIRST Module32First_;
00119 MODULE32_NEST Module32Next_;
00120
00121 #define DUMP_SIZE_MAX 8000 //max size of our dump
00122 #define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls
00123 #define NL L"\r\n" //new line
00124
00125 BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr);
00126
00127
00128 void printError( CHAR* msg )
00129 {
00130 DWORD eNum;
00131 TCHAR sysMsg[256];
00132 TCHAR* p;
00133
00134 eNum = GetLastError( );
00135 FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
00136 NULL, eNum,
00137 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
00138 sysMsg, 256, NULL );
00139
00140
00141 p = sysMsg;
00142 while( ( *p > 31 ) || ( *p == 9 ) )
00143 ++p;
00144 do { *p-- = 0; } while( ( p >= sysMsg ) &&
00145 ( ( *p == '.' ) || ( *p < 33 ) ) );
00146
00147
00148 printf( "\n WARNING: %s failed with error %d (%s)", msg, eNum, sysMsg );
00149 }
00150
00151 BOOL GetProcessThreadIDs(DWORD process_id, std::vector<DWORD>& thread_ids)
00152 {
00153 HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
00154 THREADENTRY32 te32;
00155
00156
00157 hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
00158 if( hThreadSnap == INVALID_HANDLE_VALUE )
00159 return( FALSE );
00160
00161
00162 te32.dwSize = sizeof(THREADENTRY32 );
00163
00164
00165
00166 if( !Thread32First( hThreadSnap, &te32 ) )
00167 {
00168 printError( "Thread32First" );
00169 CloseHandle( hThreadSnap );
00170 return( FALSE );
00171 }
00172
00173
00174
00175
00176 do
00177 {
00178 if( te32.th32OwnerProcessID == process_id )
00179 {
00180 thread_ids.push_back(te32.th32ThreadID);
00181 }
00182 } while( Thread32Next(hThreadSnap, &te32 ) );
00183
00184
00185 CloseHandle( hThreadSnap );
00186 return( TRUE );
00187 }
00188
00189 void WINAPI GetCallStackData(const CONTEXT* context_struct, LLSD& info)
00190 {
00191
00192
00193
00194
00195 LPWSTR Module_Name = new WCHAR[MAX_PATH];
00196 PBYTE Module_Addr = 0;
00197
00198 typedef struct STACK
00199 {
00200 STACK * Ebp;
00201 PBYTE Ret_Addr;
00202 DWORD Param[0];
00203 } STACK, * PSTACK;
00204
00205 PSTACK Ebp;
00206
00207 if(context_struct)
00208 {
00209 Ebp = (PSTACK)context_struct->Ebp;
00210 }
00211 else
00212 {
00213
00214
00215 Ebp = (PSTACK)&context_struct - 1;
00216
00217
00218 if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
00219 Ebp = Ebp->Ebp;
00220 }
00221
00222
00223
00224 for (int Ret_Addr_I = 0, i = 0;
00225 (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
00226 Ret_Addr_I++, Ebp = Ebp->Ebp, ++i)
00227 {
00228
00229
00230 if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
00231 {
00232
00233 info["CallStack"][i]["ModuleName"] = ll_convert_wide_to_string(Module_Name);
00234 info["CallStack"][i]["ModuleAddress"] = (int)Module_Addr;
00235 info["CallStack"][i]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr);
00236
00237 LLSD params;
00238
00239 if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
00240 {
00241 for(int j = 0; j < 5; ++j)
00242 {
00243 params[j] = (int)Ebp->Param[j];
00244 }
00245 }
00246 info["CallStack"][i]["Parameters"] = params;
00247 }
00248 info["CallStack"][i]["ReturnAddress"] = (int)Ebp->Ret_Addr;
00249 }
00250 }
00251
00252 BOOL GetThreadCallStack(DWORD thread_id, LLSD& info)
00253 {
00254 if(GetCurrentThreadId() == thread_id)
00255 {
00256
00257
00258
00259 return false;
00260 }
00261
00262 HANDLE thread_handle = INVALID_HANDLE_VALUE;
00263 thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id);
00264 if(INVALID_HANDLE_VALUE == thread_handle)
00265 {
00266 return FALSE;
00267 }
00268
00269 BOOL result = false;
00270 if(-1 != SuspendThread(thread_handle))
00271 {
00272 CONTEXT context_struct;
00273 context_struct.ContextFlags = CONTEXT_FULL;
00274 if(GetThreadContext(thread_handle, &context_struct))
00275 {
00276 GetCallStackData(&context_struct, info);
00277 result = true;
00278 }
00279 ResumeThread(thread_handle);
00280 }
00281 else
00282 {
00283
00284 }
00285
00286 CloseHandle(thread_handle);
00287 return result;
00288 }
00289
00290
00291
00292
00293
00294
00295 BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr)
00296
00297
00298
00299
00300 {
00301 MODULEENTRY32 M = {sizeof(M)};
00302 HANDLE hSnapshot;
00303
00304 bool found = false;
00305
00306 if (CreateToolhelp32Snapshot_)
00307 {
00308 hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);
00309
00310 if ((hSnapshot != INVALID_HANDLE_VALUE) &&
00311 Module32First_(hSnapshot, &M))
00312 {
00313 do
00314 {
00315 if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
00316 {
00317 lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
00318 Module_Addr = M.modBaseAddr;
00319 found = true;
00320 break;
00321 }
00322 } while (Module32Next_(hSnapshot, &M));
00323 }
00324
00325 CloseHandle(hSnapshot);
00326 }
00327
00328 return found;
00329 }
00330
00331
00332 void WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, LLSD& info)
00333
00334
00335
00336
00337 {
00338 LPWSTR Module_Name = new WCHAR[MAX_PATH];
00339 PBYTE Module_Addr = 0;
00340
00341 typedef struct STACK
00342 {
00343 STACK * Ebp;
00344 PBYTE Ret_Addr;
00345 DWORD Param[0];
00346 } STACK, * PSTACK;
00347
00348 STACK Stack = {0, 0};
00349 PSTACK Ebp;
00350
00351 if (pException)
00352 {
00353 Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
00354 Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
00355 Ebp = &Stack;
00356 }
00357 else
00358 {
00359 Ebp = (PSTACK)&pException - 1;
00360
00361
00362 if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
00363 Ebp = Ebp->Ebp;
00364 }
00365
00366
00367
00368 for (int Ret_Addr_I = 0, i = 0;
00369 (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
00370 Ret_Addr_I++, Ebp = Ebp->Ebp, ++i)
00371 {
00372
00373
00374 if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
00375 {
00376
00377 info["CallStack"][i]["ModuleName"] = ll_convert_wide_to_string(Module_Name);
00378 info["CallStack"][i]["ModuleAddress"] = (int)Module_Addr;
00379 info["CallStack"][i]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr);
00380
00381 LLSD params;
00382
00383 if (pException && !Ret_Addr_I)
00384 params[0] = "Exception Offset";
00385 else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
00386 {
00387 for(int j = 0; j < 5; ++j)
00388 {
00389 params[j] = (int)Ebp->Param[j];
00390 }
00391 }
00392 info["CallStack"][i]["Parameters"] = params;
00393 }
00394 info["CallStack"][i]["ReturnAddress"] = (int)Ebp->Ret_Addr;
00395 }
00396 }
00397
00398
00399 void WINAPI Get_Version_Str(LLSD& info)
00400
00401
00402 {
00403 OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)};
00404
00405 if (!GetVersionEx((POSVERSIONINFO)&V))
00406 {
00407 ZeroMemory(&V, sizeof(V));
00408 V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00409 GetVersionEx((POSVERSIONINFO)&V);
00410 }
00411
00412 if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
00413 V.dwBuildNumber = LOWORD(V.dwBuildNumber);
00414
00415 info["Platform"] = llformat("Windows: %d.%d.%d, SP %d.%d, Product Type %d",
00416 V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType);
00417 }
00418
00419
00420 LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
00421
00422
00423 {
00424 LLSD info;
00425 LPWSTR Str;
00426 int Str_Len;
00427
00428 LPWSTR Module_Name = new WCHAR[MAX_PATH];
00429 PBYTE Module_Addr;
00430 HANDLE hFile;
00431 FILETIME Last_Write_Time;
00432 FILETIME Local_File_Time;
00433 SYSTEMTIME T;
00434
00435 Str = new WCHAR[DUMP_SIZE_MAX];
00436 Str_Len = 0;
00437 if (!Str)
00438 return NULL;
00439
00440 Get_Version_Str(info);
00441
00442 GetModuleFileName(NULL, Str, MAX_PATH);
00443 info["Process"] = ll_convert_wide_to_string(Str);
00444
00445
00446 if (pException)
00447 {
00448 EXCEPTION_RECORD & E = *pException->ExceptionRecord;
00449 CONTEXT & C = *pException->ContextRecord;
00450
00451
00452 if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
00453 {
00454 info["Module"] = ll_convert_wide_to_string(Module_Name);
00455
00456 if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
00457 FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
00458 {
00459 if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
00460 {
00461 FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
00462 FileTimeToSystemTime(&Local_File_Time, &T);
00463
00464 info["DateModified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear);
00465 }
00466 CloseHandle(hFile);
00467 }
00468 }
00469 else
00470 {
00471 info["ExceptionAddr"] = (int)E.ExceptionAddress;
00472 }
00473
00474 info["ExceptionCode"] = (int)E.ExceptionCode;
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496 LLSD registers;
00497 registers["EAX"] = (int)C.Eax;
00498 registers["EBX"] = (int)C.Ebx;
00499 registers["ECX"] = (int)C.Ecx;
00500 registers["EDX"] = (int)C.Edx;
00501 registers["ESI"] = (int)C.Esi;
00502 registers["EDI"] = (int)C.Edi;
00503 registers["ESP"] = (int)C.Esp;
00504 registers["EBP"] = (int)C.Ebp;
00505 registers["EIP"] = (int)C.Eip;
00506 registers["EFlags"] = (int)C.EFlags;
00507 info["Registers"] = registers;
00508 }
00509
00510
00511 Get_Call_Stack(pException, info);
00512
00513 return info;
00514 }
00515
00516 #define UNICODE
00517
00518
00519 class LLMemoryReserve {
00520 public:
00521 LLMemoryReserve();
00522 ~LLMemoryReserve();
00523 void reserve();
00524 void release();
00525 protected:
00526 unsigned char *mReserve;
00527 static const size_t MEMORY_RESERVATION_SIZE;
00528 };
00529
00530 LLMemoryReserve::LLMemoryReserve() :
00531 mReserve(NULL)
00532 {
00533 };
00534
00535 LLMemoryReserve::~LLMemoryReserve()
00536 {
00537 release();
00538 }
00539
00540
00541 const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024;
00542
00543 void LLMemoryReserve::reserve()
00544 {
00545 if(NULL == mReserve)
00546 mReserve = new unsigned char[MEMORY_RESERVATION_SIZE];
00547 };
00548
00549 void LLMemoryReserve::release()
00550 {
00551 delete [] mReserve;
00552 mReserve = NULL;
00553 };
00554
00555 static LLMemoryReserve gEmergencyMemoryReserve;
00556
00557
00558 void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
00559 {
00560
00561 static bool s_first_run = true;
00562
00563
00564
00565 if (s_first_run)
00566 {
00567
00568 std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir());
00569
00570 HMODULE hDll = NULL;
00571 hDll = LoadLibraryA(local_dll_name.c_str());
00572 if (!hDll)
00573 {
00574 hDll = LoadLibrary(L"dbghelp.dll");
00575 }
00576
00577 if (!hDll)
00578 {
00579 LL_WARNS("AppInit") << "Couldn't find dbghelp.dll!" << LL_ENDL;
00580 }
00581 else
00582 {
00583 f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
00584
00585 if (!f_mdwp)
00586 {
00587 FreeLibrary(hDll);
00588 hDll = NULL;
00589 }
00590 }
00591
00592 gEmergencyMemoryReserve.reserve();
00593
00594 s_first_run = false;
00595 }
00596
00597
00598 HMODULE hKernel32;
00599 hKernel32 = GetModuleHandle(_T("KERNEL32"));
00600 CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
00601 Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW");
00602 Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW");
00603
00604 LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
00605 prev_filter = SetUnhandledExceptionFilter(filter_func);
00606
00607 if(prev_filter != gFilterFunc)
00608 {
00609 LL_WARNS("AppInit")
00610 << "Replacing unknown exception (" << (void *)prev_filter << ") with (" << (void *)filter_func << ") !" << LL_ENDL;
00611 }
00612
00613 gFilterFunc = filter_func;
00614 }
00615
00616 bool LLWinDebug::checkExceptionHandler()
00617 {
00618 bool ok = true;
00619 LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
00620 prev_filter = SetUnhandledExceptionFilter(gFilterFunc);
00621
00622 if (prev_filter != gFilterFunc)
00623 {
00624 LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with " << prev_filter << "!" << LL_ENDL;
00625 ok = false;
00626 }
00627
00628 if (prev_filter == NULL)
00629 {
00630 ok = FALSE;
00631 if (gFilterFunc == NULL)
00632 {
00633 LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
00634 }
00635 else
00636 {
00637 LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with NULL!" << LL_ENDL;
00638 }
00639 }
00640
00641 return ok;
00642 }
00643
00644 void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename)
00645 {
00646 if(f_mdwp == NULL || gDirUtilp == NULL)
00647 {
00648 return;
00649
00650 }
00651 else
00652 {
00653 std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
00654 filename);
00655
00656 HANDLE hFile = CreateFileA(dump_path.c_str(),
00657 GENERIC_WRITE,
00658 FILE_SHARE_WRITE,
00659 NULL,
00660 CREATE_ALWAYS,
00661 FILE_ATTRIBUTE_NORMAL,
00662 NULL);
00663
00664 if (hFile != INVALID_HANDLE_VALUE)
00665 {
00666
00667 f_mdwp(GetCurrentProcess(),
00668 GetCurrentProcessId(),
00669 hFile,
00670 type,
00671 ExInfop,
00672 NULL,
00673 NULL);
00674
00675 CloseHandle(hFile);
00676 }
00677
00678 }
00679 }
00680
00681
00682 void LLWinDebug::generateCrashStacks(struct _EXCEPTION_POINTERS *exception_infop)
00683 {
00684
00685
00686
00687
00688
00689
00690
00691
00692 LLSD info;
00693 std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
00694 "SecondLifeException");
00695 std::string log_path = dump_path + ".log";
00696
00697 if (exception_infop)
00698 {
00699
00700 gEmergencyMemoryReserve.release();
00701
00702 if(gSavedSettings.getControl("SaveMinidump") != NULL && gSavedSettings.getBOOL("SaveMinidump"))
00703 {
00704 _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
00705
00706 ExInfo.ThreadId = ::GetCurrentThreadId();
00707 ExInfo.ExceptionPointers = exception_infop;
00708 ExInfo.ClientPointers = NULL;
00709
00710 writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
00711 writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
00712 }
00713
00714 info = Get_Exception_Info(exception_infop);
00715 }
00716
00717 LLSD threads;
00718 std::vector<DWORD> thread_ids;
00719 GetProcessThreadIDs(GetCurrentProcessId(), thread_ids);
00720
00721 for(std::vector<DWORD>::iterator th_itr = thread_ids.begin();
00722 th_itr != thread_ids.end();
00723 ++th_itr)
00724 {
00725 LLSD thread_info;
00726 if(*th_itr != GetCurrentThreadId())
00727 {
00728 GetThreadCallStack(*th_itr, thread_info);
00729 }
00730
00731 if(thread_info)
00732 {
00733 threads[llformat("ID %d", *th_itr)] = thread_info;
00734 }
00735 }
00736
00737 info["Threads"] = threads;
00738
00739 std::ofstream out_file(log_path.c_str());
00740 LLSDSerialize::toPrettyXML(info, out_file);
00741 out_file.close();
00742 }
00743
00744 #endif