aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llwindebug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llwindebug.cpp')
-rw-r--r--linden/indra/newview/llwindebug.cpp406
1 files changed, 325 insertions, 81 deletions
diff --git a/linden/indra/newview/llwindebug.cpp b/linden/indra/newview/llwindebug.cpp
index b65262c..d0129c8 100644
--- a/linden/indra/newview/llwindebug.cpp
+++ b/linden/indra/newview/llwindebug.cpp
@@ -33,16 +33,69 @@
33 33
34#ifdef LL_WINDOWS 34#ifdef LL_WINDOWS
35 35
36#include <tchar.h>
37#include <tlhelp32.h>
38#include "llappviewer.h"
36#include "llwindebug.h" 39#include "llwindebug.h"
37#include "llviewercontrol.h" 40#include "llviewercontrol.h"
38#include "lldir.h" 41#include "lldir.h"
42#include "llsd.h"
43#include "llsdserialize.h"
44
45#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union
46#pragma warning(disable: 4100) //unreferenced formal parameter
47
48/*
49LLSD Block for Windows Dump Information
50<llsd>
51 <map>
52 <key>Platform</key>
53 <string></string>
54 <key>Process</key>
55 <string></string>
56 <key>Module</key>
57 <string></string>
58 <key>DateModified</key>
59 <string></string>
60 <key>ExceptionCode</key>
61 <string></string>
62 <key>ExceptionRead/WriteAddress</key>
63 <string></string>
64 <key>Instruction</key>
65 <string></string>
66 <key>Registers</key>
67 <map>
68 <!-- Continued for all registers -->
69 <key>EIP</key>
70 <string>...</string>
71 <!-- ... -->
72 </map>
73 <key>Call Stack</key>
74 <array>
75 <!-- One map per stack frame -->
76 <map>
77 <key>ModuleName</key>
78 <string></string>
79 <key>ModuleBaseAddress</key>
80 <string></string>
81 <key>ModuleOffsetAddress</key>
82 <string></string>
83 <key>Parameters</key>
84 <array>
85 <string></string>
86 </array>
87 </map>
88 <!-- ... -->
89 </array>
90 </map>
91</llsd>
92
93*/
39 94
40// From viewer.h 95// From viewer.h
41extern BOOL gInProductionGrid; 96extern BOOL gInProductionGrid;
42 97
43extern void (*gCrashCallback)(void); 98extern void (*gCrashCallback)(void);
44extern void write_debug(const char *str);
45extern void write_debug(const std::string &str);
46 99
47// based on dbghelp.h 100// based on dbghelp.h
48typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, 101typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
@@ -53,6 +106,248 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF
53 106
54MINIDUMPWRITEDUMP f_mdwp = NULL; 107MINIDUMPWRITEDUMP f_mdwp = NULL;
55 108
109#undef UNICODE
110
111HMODULE hDbgHelp;
112
113// Tool Help functions.
114typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
115typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
116typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
117
118CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
119MODULE32_FIRST Module32First_;
120MODULE32_NEST Module32Next_;
121
122#define DUMP_SIZE_MAX 8000 //max size of our dump
123#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls
124#define NL L"\r\n" //new line
125
126//Windows Call Stack Construction idea from
127//http://www.codeproject.com/tools/minidump.asp
128
129//****************************************************************************************
130BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr)
131//****************************************************************************************
132// Find module by Ret_Addr (address in the module).
133// Return Module_Name (full path) and Module_Addr (start address).
134// Return TRUE if found.
135{
136 MODULEENTRY32 M = {sizeof(M)};
137 HANDLE hSnapshot;
138
139 bool found = false;
140
141 if (CreateToolhelp32Snapshot_)
142 {
143 hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);
144
145 if ((hSnapshot != INVALID_HANDLE_VALUE) &&
146 Module32First_(hSnapshot, &M))
147 {
148 do
149 {
150 if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
151 {
152 lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
153 Module_Addr = M.modBaseAddr;
154 found = true;
155 break;
156 }
157 } while (Module32Next_(hSnapshot, &M));
158 }
159
160 CloseHandle(hSnapshot);
161 }
162
163 return found;
164} //Get_Module_By_Ret_Addr
165
166//******************************************************************
167void WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, LLSD& info)
168//******************************************************************
169// Fill Str with call stack info.
170// pException can be either GetExceptionInformation() or NULL.
171// If pException = NULL - get current call stack.
172{
173 LPWSTR Module_Name = new WCHAR[MAX_PATH];
174 PBYTE Module_Addr = 0;
175
176 typedef struct STACK
177 {
178 STACK * Ebp;
179 PBYTE Ret_Addr;
180 DWORD Param[0];
181 } STACK, * PSTACK;
182
183 STACK Stack = {0, 0};
184 PSTACK Ebp;
185
186 if (pException) //fake frame for exception address
187 {
188 Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
189 Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
190 Ebp = &Stack;
191 }
192 else
193 {
194 Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack()
195
196 // Skip frame of Get_Call_Stack().
197 if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
198 Ebp = Ebp->Ebp; //caller ebp
199 }
200
201 // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
202 // Break trace on wrong stack frame.
203 for (int Ret_Addr_I = 0, i = 0;
204 (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
205 Ret_Addr_I++, Ebp = Ebp->Ebp, ++i)
206 {
207 // If module with Ebp->Ret_Addr found.
208
209 if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
210 {
211 // Save module's address and full path.
212 info["CallStack"][i]["ModuleName"] = ll_convert_wide_to_string(Module_Name);
213 info["CallStack"][i]["ModuleAddress"] = (int)Module_Addr;
214 info["CallStack"][i]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr);
215
216 LLSD params;
217 // Save 5 params of the call. We don't know the real number of params.
218 if (pException && !Ret_Addr_I) //fake frame for exception address
219 params[0] = "Exception Offset";
220 else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
221 {
222 for(int j = 0; j < 5; ++j)
223 {
224 params[j] = (int)Ebp->Param[j];
225 }
226 }
227 info["CallStack"][i]["Parameters"] = params;
228 }
229 info["CallStack"][i]["ReturnAddress"] = (int)Ebp->Ret_Addr;
230 }
231} //Get_Call_Stack
232
233//***********************************
234void WINAPI Get_Version_Str(LLSD& info)
235//***********************************
236// Fill Str with Windows version.
237{
238 OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later
239
240 if (!GetVersionEx((POSVERSIONINFO)&V))
241 {
242 ZeroMemory(&V, sizeof(V));
243 V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
244 GetVersionEx((POSVERSIONINFO)&V);
245 }
246
247 if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
248 V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx
249
250 info["Platform"] = llformat("Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,...
251 V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType);
252} //Get_Version_Str
253
254//*************************************************************
255LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
256//*************************************************************
257// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
258{
259 LLSD info;
260 LPWSTR Str;
261 int Str_Len;
262 int i;
263 LPWSTR Module_Name = new WCHAR[MAX_PATH];
264 PBYTE Module_Addr;
265 HANDLE hFile;
266 FILETIME Last_Write_Time;
267 FILETIME Local_File_Time;
268 SYSTEMTIME T;
269
270 Str = new WCHAR[DUMP_SIZE_MAX];
271 Str_Len = 0;
272 if (!Str)
273 return NULL;
274
275 Get_Version_Str(info);
276
277 GetModuleFileName(NULL, Str, MAX_PATH);
278 info["Process"] = ll_convert_wide_to_string(Str);
279
280 // If exception occurred.
281 if (pException)
282 {
283 EXCEPTION_RECORD & E = *pException->ExceptionRecord;
284 CONTEXT & C = *pException->ContextRecord;
285
286 // If module with E.ExceptionAddress found - save its path and date.
287 if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
288 {
289 info["Module"] = ll_convert_wide_to_string(Module_Name);
290
291 if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
292 FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
293 {
294 if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
295 {
296 FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
297 FileTimeToSystemTime(&Local_File_Time, &T);
298
299 info["DateModified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear);
300 }
301 CloseHandle(hFile);
302 }
303 }
304 else
305 {
306 info["ExceptionAddr"] = (int)E.ExceptionAddress;
307 }
308
309 info["ExceptionCode"] = (int)E.ExceptionCode;
310
311 /*
312 //TODO: Fix this
313 if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
314 {
315 // Access violation type - Write/Read.
316 LLSD exception_info;
317 exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read";
318 exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]);
319 info["Exception Information"] = exception_info;
320 }
321 */
322
323
324 // Save instruction that caused exception.
325 LLString str;
326 for (i = 0; i < 16; i++)
327 str += llformat(" %02X", PBYTE(E.ExceptionAddress)[i]);
328 info["Instruction"] = str;
329
330 LLSD registers;
331 registers["EAX"] = (int)C.Eax;
332 registers["EBX"] = (int)C.Ebx;
333 registers["ECX"] = (int)C.Ecx;
334 registers["EDX"] = (int)C.Edx;
335 registers["ESI"] = (int)C.Esi;
336 registers["EDI"] = (int)C.Edi;
337 registers["ESP"] = (int)C.Esp;
338 registers["EBP"] = (int)C.Ebp;
339 registers["EIP"] = (int)C.Eip;
340 registers["EFlags"] = (int)C.EFlags;
341 info["Registers"] = registers;
342 } //if (pException)
343
344 // Save call stack info.
345 Get_Call_Stack(pException, info);
346
347 return info;
348} //Get_Exception_Info
349
350#define UNICODE
56 351
57 352
58class LLMemoryReserve { 353class LLMemoryReserve {
@@ -96,7 +391,6 @@ static LLMemoryReserve gEmergencyMemoryReserve;
96// static 391// static
97BOOL LLWinDebug::setupExceptionHandler() 392BOOL LLWinDebug::setupExceptionHandler()
98{ 393{
99#ifdef LL_RELEASE_FOR_DOWNLOAD
100 394
101 static BOOL s_first_run = TRUE; 395 static BOOL s_first_run = TRUE;
102 // Load the dbghelp dll now, instead of waiting for the crash. 396 // Load the dbghelp dll now, instead of waiting for the crash.
@@ -123,7 +417,7 @@ BOOL LLWinDebug::setupExceptionHandler()
123 msg += local_dll_name; 417 msg += local_dll_name;
124 msg += "!\n"; 418 msg += "!\n";
125 419
126 write_debug(msg.c_str()); 420 //write_debug(msg.c_str());
127 421
128 ok = FALSE; 422 ok = FALSE;
129 } 423 }
@@ -133,7 +427,7 @@ BOOL LLWinDebug::setupExceptionHandler()
133 427
134 if (!f_mdwp) 428 if (!f_mdwp)
135 { 429 {
136 write_debug("No MiniDumpWriteDump!\n"); 430 //write_debug("No MiniDumpWriteDump!\n");
137 FreeLibrary(hDll); 431 FreeLibrary(hDll);
138 hDll = NULL; 432 hDll = NULL;
139 ok = FALSE; 433 ok = FALSE;
@@ -146,6 +440,13 @@ BOOL LLWinDebug::setupExceptionHandler()
146 LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; 440 LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
147 prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException); 441 prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
148 442
443 // Try to get Tool Help library functions.
444 HMODULE hKernel32;
445 hKernel32 = GetModuleHandle(_T("KERNEL32"));
446 CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
447 Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW");
448 Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW");
449
149 if (s_first_run) 450 if (s_first_run)
150 { 451 {
151 // We're fine, this is the first run. 452 // We're fine, this is the first run.
@@ -162,56 +463,16 @@ BOOL LLWinDebug::setupExceptionHandler()
162 llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl; 463 llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
163 ok = FALSE; 464 ok = FALSE;
164 } 465 }
466
165 return ok; 467 return ok;
166#else
167 // Internal builds don't mess with exception handling. 468 // Internal builds don't mess with exception handling.
168 return TRUE; 469 //return TRUE;
169#endif
170}
171
172void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename)
173{
174 if(f_mdwp == NULL)
175 {
176 write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
177 }
178 else if(gDirUtilp == NULL)
179 {
180 write_debug("No way to generate a minidump, no gDirUtilp!\n");
181 }
182 else
183 {
184 std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
185 filename);
186
187 HANDLE hFile = CreateFileA(dump_path.c_str(),
188 GENERIC_WRITE,
189 FILE_SHARE_WRITE,
190 NULL,
191 CREATE_ALWAYS,
192 FILE_ATTRIBUTE_NORMAL,
193 NULL);
194
195 if (hFile != INVALID_HANDLE_VALUE)
196 {
197 // Write the dump, ignoring the return value
198 f_mdwp(GetCurrentProcess(),
199 GetCurrentProcessId(),
200 hFile,
201 type,
202 ExInfop,
203 NULL,
204 NULL);
205
206 CloseHandle(hFile);
207 }
208
209 }
210} 470}
211
212// static 471// static
213LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) 472LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
214{ 473{
474 // *NOTE:Mani - This method is no longer the initial exception handler.
475 // It is called from viewer_windows_exception_handler() and other places.
215 476
216 // 477 //
217 // Let go of a bunch of reserved memory to give library calls etc 478 // Let go of a bunch of reserved memory to give library calls etc
@@ -220,50 +481,34 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
220 // 481 //
221 gEmergencyMemoryReserve.release(); 482 gEmergencyMemoryReserve.release();
222 483
223 BOOL userWantsMaxiDump =
224 (stricmp(gSavedSettings.getString("LastName").c_str(), "linden") == 0)
225 || (stricmp(gSavedSettings.getString("LastName").c_str(), "tester") == 0);
226
227 BOOL alsoSaveMaxiDump = userWantsMaxiDump && !gInProductionGrid;
228
229 /* Calculate alsoSaveMaxiDump here */
230
231 if (exception_infop) 484 if (exception_infop)
232 { 485 {
233 _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
234 486
235 ExInfo.ThreadId = ::GetCurrentThreadId(); 487 std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
236 ExInfo.ExceptionPointers = exception_infop; 488 "SecondLifeException");
237 ExInfo.ClientPointers = NULL;
238 489
239 writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp"); 490 std::string log_path = dump_path + ".log";
240 491
241 if(alsoSaveMaxiDump) 492 LLSD info;
242 writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp"); 493 info = Get_Exception_Info(exception_infop);
494 if (info)
495 {
496 std::ofstream out_file(log_path.c_str());
497 LLSDSerialize::toPrettyXML(info, out_file);
498 out_file.close();
499 }
243 } 500 }
244 else 501 else
245 { 502 {
246 writeDumpToFile(MiniDumpNormal, NULL, "SecondLife.dmp");
247
248 if(alsoSaveMaxiDump)
249 writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), NULL, "SecondLifePlus.dmp");
250 }
251
252 if (!exception_infop)
253 {
254 // We're calling this due to a network error, not due to an actual exception. 503 // We're calling this due to a network error, not due to an actual exception.
255 // It doesn't realy matter what we return. 504 // It doesn't realy matter what we return.
256 return EXCEPTION_CONTINUE_SEARCH; 505 return EXCEPTION_CONTINUE_SEARCH;
257 } 506 }
258 507
259 // 508 //handle viewer crash must be called here since
260 // Call the newview crash callback, which will spawn the crash 509 //we don't return handling of the application
261 // reporter. It may or may not spawn a dialog. 510 //back to the process.
262 // 511 LLAppViewer::handleViewerCrash();
263 if (gCrashCallback)
264 {
265 gCrashCallback();
266 }
267 512
268 // 513 //
269 // At this point, we always want to exit the app. There's no graceful 514 // At this point, we always want to exit the app. There's no graceful
@@ -276,4 +521,3 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
276} 521}
277 522
278#endif 523#endif
279