1 // Copyright 2012 Idol Software, Inc.
3 // This file is part of CrashHandler library.
5 // CrashHandler library is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "CrashHandlerExport.h"
21 #include "..\SendRpt\Config.h"
22 #include "..\SendRpt\Serializer.h"
26 HINSTANCE g_hThisDLL
= NULL
;
27 bool g_applicationVerifierPresent
= false;
28 bool g_ownProcess
= false;
29 volatile LONG g_insideCrashHandler
= 0;
30 LPTOP_LEVEL_EXCEPTION_FILTER g_prevTopLevelExceptionFilter
= NULL
;
32 // It should not be destroyed on PROCESS_DETACH, because it may be used later if someone will crash on deinitialization
33 // so it is on heap, but not static (runtime destroy static objects on PROCESS_DETACH)
34 Config
* g_pConfig
= new Config();
36 DWORD g_tlsPrevTerminatePtr
= TLS_OUT_OF_INDEXES
;
37 typedef terminate_function (__cdecl
*pfn_set_terminate
)(terminate_function
);
38 pfn_set_terminate g_set_terminate
= NULL
;
40 enum CrashServerCustomExceptionCodes
42 CRASHSERVER_EXCEPTION_ASSERTION_VIOLATED
= CrashHandler::ExceptionAssertionViolated
, // 0xCCE17000
43 CRASHSERVER_EXCEPTION_CPP_TERMINATE
= 0xCCE17001,
44 CRASHSERVER_EXCEPTION_CPP_PURE_CALL
= 0xCCE17002,
45 CRASHSERVER_EXCEPTION_CPP_INVALID_PARAMETER
= 0xCCE17003,
48 void ExtractFileFromResource(HMODULE hModule
, DWORD resId
, LPCTSTR path
)
50 HRSRC hDbghelpRes
= FindResource(hModule
, MAKEINTRESOURCE(resId
), RT_RCDATA
);
52 throw runtime_error("failed to find file in resources");
54 HGLOBAL hDbghelpGlobal
= LoadResource(hModule
, hDbghelpRes
);
56 throw runtime_error("failed to load file from resources");
58 CAtlFile
hFile(CreateFile(path
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
));
59 if (hFile
== INVALID_HANDLE_VALUE
)
60 throw runtime_error("failed to create file");
62 if (FAILED(hFile
.Write(LockResource(hDbghelpGlobal
), SizeofResource(hModule
, hDbghelpRes
))))
63 throw runtime_error("failed to write file");
66 DWORD WINAPI
SendReportThread(LPVOID lpParameter
)
68 InterlockedIncrement(&g_insideCrashHandler
);
70 //::MessageBoxA(0, "SendReportThread", "SendReportThread", 0);
71 MINIDUMP_EXCEPTION_INFORMATION
* pExceptInfo
= (MINIDUMP_EXCEPTION_INFORMATION
*)lpParameter
;
74 WCHAR sendRptExe
[MAX_PATH
], drive
[4], dir
[MAX_PATH
], fname
[MAX_PATH
], ext
[MAX_PATH
];
75 GetModuleFileNameW(NULL
, sendRptExe
, _countof(sendRptExe
));
76 _wsplitpath_s(sendRptExe
, drive
, dir
, fname
, ext
);
77 _wmakepath_s(sendRptExe
, drive
, dir
, L
"SendRpt", L
".exe");
78 bool localSendRpt
= 0 == _waccess_s(sendRptExe
, 00/* Existence only */);
81 GetTempPathW(_countof(sendRptExe
), sendRptExe
);
82 wcscat_s(sendRptExe
, L
"SendRpt.exe");
84 ExtractFileFromResource(g_hThisDLL
, IDR_SENDRPT
, sendRptExe
);
88 cmd
.Format(_T("\"%s\" "), sendRptExe
);
91 DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &hProcess
.m_h
, PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
| THREAD_ALL_ACCESS
, TRUE
, 0);
93 CHandle
hReportReady(CreateEvent(NULL
, TRUE
, FALSE
, NULL
));
94 SetHandleInformation(hReportReady
, HANDLE_FLAG_INHERIT
, HANDLE_FLAG_INHERIT
);
97 param
.Process
= hProcess
;
98 param
.ProcessId
= GetCurrentProcessId();
99 param
.ExceptInfo
= *pExceptInfo
;
100 param
.WasAssert
= pExceptInfo
->ExceptionPointers
->ExceptionRecord
->ExceptionCode
== CRASHSERVER_EXCEPTION_ASSERTION_VIOLATED
;
101 param
.ReportReady
= hReportReady
;
104 ser
<< param
<< *g_pConfig
;
105 cmd
.Append(ser
.GetHex());
107 STARTUPINFO si
= {sizeof(si
)};
108 PROCESS_INFORMATION pi
;
109 if (!CreateProcess(NULL
, cmd
.GetBuffer(), NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &si
, &pi
))
110 throw std::runtime_error("failed to start SendRpt");
111 CloseHandle(pi
.hThread
);
113 HANDLE handles
[] = { pi
.hProcess
, hReportReady
.m_h
};
114 DWORD res
= WaitForMultipleObjects(_countof(handles
), handles
, FALSE
, INFINITE
);
116 CloseHandle(pi
.hProcess
);
118 // if hReportReady event signaled, SendRpt is still working, so delete only when SendRpt has finished.
119 if (!localSendRpt
&& handles
[res
- WAIT_OBJECT_0
] == pi
.hProcess
)
120 DeleteFileW(sendRptExe
);
122 InterlockedDecrement(&g_insideCrashHandler
);
126 catch (std::exception
& ex
)
128 OutputDebugStringA(ex
.what());
131 InterlockedDecrement(&g_insideCrashHandler
);
136 LONG
SendReport(EXCEPTION_POINTERS
* pExceptionPointers
)
138 if (IsDebuggerPresent())
139 return EXCEPTION_CONTINUE_SEARCH
;
141 // We can enter here because of stack overflow, so lets all local variables
142 // be static, because variables on stack can lead to second stack overflow.
143 static DWORD dwThreadId
;
144 static HANDLE hThread
;
145 static MINIDUMP_EXCEPTION_INFORMATION exceptInfo
;
146 exceptInfo
.ThreadId
= GetCurrentThreadId();
147 exceptInfo
.ExceptionPointers
= pExceptionPointers
;
148 exceptInfo
.ClientPointers
= FALSE
;
150 // If stack overflow, do all processing in another thread
151 if (pExceptionPointers
!= NULL
152 && pExceptionPointers
->ExceptionRecord
!= NULL
153 && pExceptionPointers
->ExceptionRecord
->ExceptionCode
== EXCEPTION_STACK_OVERFLOW
154 && (hThread
= CreateThread(NULL
, 0, SendReportThread
, &exceptInfo
, 0, &dwThreadId
)) != NULL
)
156 WaitForSingleObject(hThread
, INFINITE
);
160 SendReportThread(&exceptInfo
);
163 if (pExceptionPointers
->ExceptionRecord
->ExceptionCode
== CRASHSERVER_EXCEPTION_ASSERTION_VIOLATED
)
164 return EXCEPTION_CONTINUE_EXECUTION
;
166 return g_pConfig
->UseWER
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER
;
169 LONG WINAPI
TopLevelExceptionFilter(EXCEPTION_POINTERS
* pExceptionPointers
)
171 if (pExceptionPointers
->ExceptionRecord
->ExceptionCode
== EXCEPTION_BREAKPOINT
172 && g_applicationVerifierPresent
)
174 // This crash has been processed in VectoredExceptionHandler and if we here we want WinQual
175 return EXCEPTION_CONTINUE_SEARCH
;
178 return SendReport(pExceptionPointers
);
181 LONG CALLBACK
VectoredExceptionHandler(EXCEPTION_POINTERS
* pExceptionPointers
)
183 // Probably this is Application Verifier stop
184 if (pExceptionPointers
->ExceptionRecord
->ExceptionCode
== EXCEPTION_BREAKPOINT
185 && g_applicationVerifierPresent
)
187 LONG res
= SendReport(pExceptionPointers
);
188 if (res
== EXCEPTION_EXECUTE_HANDLER
)
189 ExitProcess(0); // we don't want WinQual from Application Verifier
192 // Asserts should be handled in VectoredExceptionHandler since EXCEPTION_CONTINUE_EXECUTION is used
193 // and __try { ... } __except(EXCEPTION_EXECUTE_HANDLER) { ... } in the way to TopLevelExceptionFilter
194 // may break this logic.
195 if (pExceptionPointers
->ExceptionRecord
->ExceptionCode
== CRASHSERVER_EXCEPTION_ASSERTION_VIOLATED
)
196 return SendReport(pExceptionPointers
);
198 return EXCEPTION_CONTINUE_SEARCH
;
201 FARPROC
GetSetUnhandledExceptionFilterPointer()
204 // In Windows 8 SetUnhandledExceptionFilter implementation is in kernelbase
205 if (HMODULE kernelbase
= GetModuleHandle(_T("kernelbase")))
206 suef
= GetProcAddress(kernelbase
, "SetUnhandledExceptionFilter");
209 if (HMODULE kernel32
= GetModuleHandle(_T("kernel32")))
210 suef
= GetProcAddress(kernel32
, "SetUnhandledExceptionFilter");
215 void SwitchSetUnhandledExceptionFilter(bool enable
)
217 FARPROC suef
= GetSetUnhandledExceptionFilterPointer();
221 // newBody is something like that:
223 // LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER)
228 static const BYTE newBody
[] =
230 0x33, 0xC0, // xor eax,eax
233 #elif defined(_M_IX86)
234 static const BYTE newBody
[] =
236 0x8B, 0xFF, // mov edi,edi <- for hotpatching
237 0x33, 0xC0, // xor eax,eax
238 0xC2, 0x04, 0x00 // ret 4
241 # error Unsupported architecture
243 const SIZE_T bodySize
= sizeof(newBody
);
245 static BYTE oldBody
[bodySize
] = { 0 };
248 if (!VirtualProtect(suef
, bodySize
, PAGE_EXECUTE_READWRITE
, &oldProtection
))
252 memcpy(oldBody
, suef
, bodySize
);
253 memcpy(suef
, newBody
, bodySize
);
255 else if (oldBody
[0] != 0)
257 memcpy(suef
, oldBody
, bodySize
);
260 VirtualProtect(suef
, bodySize
, oldProtection
, &oldProtection
);
263 void DisableSetUnhandledExceptionFilter()
265 SwitchSetUnhandledExceptionFilter(false);
268 void ReenableSetUnhandledExceptionFilter()
270 SwitchSetUnhandledExceptionFilter(true);
273 // This code should be inplace, so it is a macro
274 #define SendReportWithCode(code) __try { RaiseException(code, EXCEPTION_NONCONTINUABLE, 0, NULL); } __except(TopLevelExceptionFilter(GetExceptionInformation())) {}
276 void CrashServer_SendAssertionViolated()
278 RaiseException(CRASHSERVER_EXCEPTION_ASSERTION_VIOLATED
, 0, 0, NULL
);
281 void CrashServer_TerminateHandler()
283 SendReportWithCode(CRASHSERVER_EXCEPTION_CPP_TERMINATE
);
285 if (g_tlsPrevTerminatePtr
!= TLS_OUT_OF_INDEXES
)
287 terminate_function prev_terminate
= static_cast<terminate_function
>(TlsGetValue(g_tlsPrevTerminatePtr
));
292 if (!g_pConfig
->UseWER
)
296 void CrashServer_PureCallHandler()
298 SendReportWithCode(CRASHSERVER_EXCEPTION_CPP_PURE_CALL
);
302 void CrashServer_InvalidParameterHandler(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t)
304 SendReportWithCode(CRASHSERVER_EXCEPTION_CPP_INVALID_PARAMETER
);
308 void CrashServer_SigAbrtHandler(int)
310 SendReportWithCode(CRASHSERVER_EXCEPTION_CPP_TERMINATE
);
314 void MakePerThreadInitialization()
316 if (g_tlsPrevTerminatePtr
!= TLS_OUT_OF_INDEXES
&& g_set_terminate
)
317 TlsSetValue(g_tlsPrevTerminatePtr
, g_set_terminate(CrashServer_TerminateHandler
));
320 void InitCrtErrorHandlers()
322 LPCTSTR crtModules
[] = {
323 _T("msvcr70"), _T("msvcr70d"),
324 _T("msvcr71"), _T("msvcr71d"),
325 _T("msvcr80"), _T("msvcr80d"),
326 _T("msvcr90"), _T("msvcr90d"),
327 _T("msvcr100"), _T("msvcr100d"),
328 _T("msvcr110"), _T("msvcr110d"),
329 _T("msvcr120"), _T("msvcr120d"),
332 HMODULE hMsvcrDll
= NULL
;
333 for (size_t i
= 0; !hMsvcrDll
&& i
< _countof(crtModules
); ++i
)
334 hMsvcrDll
= GetModuleHandle(crtModules
[i
]);
339 g_set_terminate
= (pfn_set_terminate
) GetProcAddress(hMsvcrDll
, "?set_terminate@@YAP6AXXZP6AXXZ@Z");
341 typedef _purecall_handler (__cdecl
*pfn_set_purecall_handler
)(_purecall_handler
);
342 pfn_set_purecall_handler l_set_purecall_handler
= (pfn_set_purecall_handler
) GetProcAddress(hMsvcrDll
, "_set_purecall_handler");
343 if (l_set_purecall_handler
)
344 l_set_purecall_handler(CrashServer_PureCallHandler
);
346 typedef _invalid_parameter_handler (__cdecl
*pfn_set_invalid_parameter_handler
)(_invalid_parameter_handler
);
347 pfn_set_invalid_parameter_handler l_set_invalid_parameter_handler
= (pfn_set_invalid_parameter_handler
) GetProcAddress(hMsvcrDll
, "_set_invalid_parameter_handler");
348 if (l_set_invalid_parameter_handler
)
349 l_set_invalid_parameter_handler(CrashServer_InvalidParameterHandler
);
351 typedef void (__cdecl
*pfn_signal
)(int sig
, void (__cdecl
*func
)(int));
352 pfn_signal l_signal
= (pfn_signal
) GetProcAddress(hMsvcrDll
, "signal");
354 l_signal(SIGABRT
, CrashServer_SigAbrtHandler
);
357 BOOL
InitCrashHandler(ApplicationInfo
* applicationInfo
, HandlerSettings
* handlerSettings
, BOOL ownProcess
)
359 #define IS_EXIST(field) (FIELD_OFFSET(ApplicationInfo, field) < applicationInfo->ApplicationInfoSize)
360 if (applicationInfo
== NULL
361 || applicationInfo
->ApplicationInfoSize
== 0
362 || applicationInfo
->ApplicationInfoSize
> sizeof(ApplicationInfo
)
363 || !IS_EXIST(ApplicationGUID
) || applicationInfo
->ApplicationGUID
== NULL
364 || !IS_EXIST(Prefix
) || applicationInfo
->Prefix
== NULL
365 || !IS_EXIST(AppName
) || applicationInfo
->AppName
== NULL
366 || !IS_EXIST(Company
) || applicationInfo
->Company
== NULL
)
368 SetLastError(ERROR_INVALID_PARAMETER
);
372 g_pConfig
->ApplicationGUID
= applicationInfo
->ApplicationGUID
;
373 g_pConfig
->Prefix
= applicationInfo
->Prefix
;
374 g_pConfig
->AppName
= applicationInfo
->AppName
;
375 g_pConfig
->Company
= applicationInfo
->Company
;
377 memcpy(g_pConfig
->V
, applicationInfo
->V
, sizeof(applicationInfo
->V
));
379 memset(g_pConfig
->V
, 0, sizeof(g_pConfig
->V
));
380 if (IS_EXIST(Hotfix
))
381 g_pConfig
->Hotfix
= applicationInfo
->Hotfix
;
383 g_pConfig
->Hotfix
= 0;
384 if (IS_EXIST(PrivacyPolicyUrl
) && applicationInfo
->PrivacyPolicyUrl
!= NULL
)
385 g_pConfig
->PrivacyPolicyUrl
= applicationInfo
->PrivacyPolicyUrl
;
387 g_pConfig
->PrivacyPolicyUrl
.Format(L
"http://www.crash-server.com/AppPrivacyPolicy.aspx?AppID=%s", (LPCWSTR
)g_pConfig
->ApplicationGUID
);
390 #define IS_EXIST(field) (handlerSettings != NULL && (FIELD_OFFSET(HandlerSettings, field) < handlerSettings->HandlerSettingsSize))
392 if (handlerSettings
!= NULL
393 && (handlerSettings
->HandlerSettingsSize
== 0 || handlerSettings
->HandlerSettingsSize
> sizeof(HandlerSettings
)))
395 SetLastError(ERROR_INVALID_PARAMETER
);
399 if (IS_EXIST(LeaveDumpFilesInTempFolder
))
401 g_pConfig
->ServiceMode
= handlerSettings
->LeaveDumpFilesInTempFolder
== 2; // hidden behavior (used for dumpparser)
402 g_pConfig
->LeaveDumpFilesInTempFolder
= handlerSettings
->LeaveDumpFilesInTempFolder
!= FALSE
;
406 g_pConfig
->ServiceMode
= 0;
407 g_pConfig
->LeaveDumpFilesInTempFolder
= FALSE
;
409 if (IS_EXIST(OpenProblemInBrowser
))
410 g_pConfig
->OpenProblemInBrowser
= handlerSettings
->OpenProblemInBrowser
;
412 g_pConfig
->OpenProblemInBrowser
= FALSE
;
413 if (IS_EXIST(UseWER
))
414 g_pConfig
->UseWER
= handlerSettings
->UseWER
;
416 g_pConfig
->UseWER
= FALSE
;
417 if (IS_EXIST(SubmitterID
))
418 g_pConfig
->SubmitterID
= handlerSettings
->SubmitterID
;
420 g_pConfig
->SubmitterID
= 0;
423 WCHAR path
[MAX_PATH
], drive
[MAX_PATH
], dir
[MAX_PATH
], fname
[MAX_PATH
], ext
[MAX_PATH
];
424 GetModuleFileNameW(NULL
, path
, _countof(path
));
425 _wsplitpath_s(path
, drive
, dir
, fname
, ext
);
426 g_pConfig
->ProcessName
= CStringW(fname
) + ext
;
427 g_pConfig
->ProcessName
.MakeLower();
429 g_ownProcess
= ownProcess
!= FALSE
;
431 static bool inited
= false;
436 // Application verifier sets its own VectoredExceptionHandler
437 // and Application verifier breaks redirected to WinQual.
438 // After that TopLevelExceptionFilter works.
439 // This behavior looks bad and anyway we don't need WinQual.
440 // So we set our VectoredExceptionHandler before AppVerifier and
441 // catch problems first.
442 g_applicationVerifierPresent
= GetModuleHandle(_T("verifier.dll")) != NULL
;
443 if (g_applicationVerifierPresent
)
444 AddVectoredExceptionHandler(TRUE
, VectoredExceptionHandler
);
446 g_prevTopLevelExceptionFilter
= SetUnhandledExceptionFilter(TopLevelExceptionFilter
);
447 // There is a lot of libraries that want to set their own wrong UnhandledExceptionFilter in our application.
448 // One of these is MSVCRT with __report_gsfailure and _call_reportfault leading to many
449 // of MSVCRT error reports to be redirected to Dr. Watson.
450 DisableSetUnhandledExceptionFilter();
452 InitCrtErrorHandlers();
453 MakePerThreadInitialization();
456 // Need to lock library in process.
457 // Since some crashes appears on process deinitialization and we should be ready to handle it.
458 LoadLibrary(_T("crshhndl.dll"));
465 void AddUserInfoToReport(LPCWSTR key
, LPCWSTR value
)
469 g_pConfig
->UserInfo
.push_back(make_pair(CStringW(key
), value
));
472 void AddFileToReport(LPCWSTR path
, LPCWSTR reportFileName
)
479 WCHAR drive
[MAX_PATH
], dir
[MAX_PATH
], fname
[MAX_PATH
], ext
[MAX_PATH
];
480 _wsplitpath_s(path
, drive
, dir
, fname
, ext
);
481 fileName
= CStringW(fname
) + ext
;
485 fileName
= reportFileName
;
487 g_pConfig
->FilesToAttach
.push_back(make_pair(CStringW(path
), fileName
));
490 void RemoveFileFromReport(LPCWSTR path
)
494 /* TODO: needs to be converted to work with VS2008, but atm it's never called */
496 auto it
= std::find_if(g_pConfig
->FilesToAttach
.begin(), g_pConfig
->FilesToAttach
.end(),
497 [path
](std::pair
<CStringW
, CStringW
>& x
){ return x
.first
== path
; });
498 if (it
!= g_pConfig
->FilesToAttach
.end())
499 g_pConfig
->FilesToAttach
.erase(it
);
503 BOOL
GetVersionFromFile(LPCWSTR path
, ApplicationInfo
* appInfo
)
505 if (!path
|| !appInfo
)
513 DWORD size
= GetFileVersionInfoSize(path
, NULL
);
516 LPVOID pVerInfo
= HeapAlloc(GetProcessHeap(), 0, size
);
517 if (!GetFileVersionInfo(path
, 0, size
, pVerInfo
))
519 HeapFree(GetProcessHeap(), 0, pVerInfo
);
522 VS_FIXEDFILEINFO
* lpFileinfo
= NULL
;
524 if (!VerQueryValue(pVerInfo
, _T("\\"), (LPVOID
*)&lpFileinfo
, &uLen
))
526 HeapFree(GetProcessHeap(), 0, pVerInfo
);
529 appInfo
->V
[0] = HIWORD(lpFileinfo
->dwProductVersionMS
);
530 appInfo
->V
[1] = LOWORD(lpFileinfo
->dwProductVersionMS
);
531 appInfo
->V
[2] = HIWORD(lpFileinfo
->dwProductVersionLS
);
532 appInfo
->V
[3] = LOWORD(lpFileinfo
->dwProductVersionLS
);
533 HeapFree(GetProcessHeap(), 0, pVerInfo
);
538 BOOL
GetVersionFromApp(ApplicationInfo
* appInfo
)
548 TCHAR path
[MAX_PATH
];
549 if (!GetModuleFileName(NULL
, path
, _countof(path
)))
552 return GetVersionFromFile(path
, appInfo
);
557 return g_insideCrashHandler
== 0 ? TRUE
: FALSE
;
560 BOOL WINAPI
DllMain(HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)
564 case DLL_PROCESS_ATTACH
:
565 g_hThisDLL
= hinstDLL
;
566 g_tlsPrevTerminatePtr
= TlsAlloc();
568 case DLL_PROCESS_DETACH
:
569 if (g_tlsPrevTerminatePtr
!= TLS_OUT_OF_INDEXES
)
571 TlsFree(g_tlsPrevTerminatePtr
);
572 g_tlsPrevTerminatePtr
= TLS_OUT_OF_INDEXES
;
574 if (g_prevTopLevelExceptionFilter
)
576 ReenableSetUnhandledExceptionFilter();
577 SetUnhandledExceptionFilter(g_prevTopLevelExceptionFilter
);
579 if (g_applicationVerifierPresent
)
580 RemoveVectoredExceptionHandler(VectoredExceptionHandler
);
582 case DLL_THREAD_ATTACH
:
585 MakePerThreadInitialization();