1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013 - TortoiseGit
4 // Copyright (C) 2012 - 2014 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "../../ext/CrashServer/CrashHandler/CrashHandler/CrashHandler.h"
27 // dummy define, needed only when we use crashrpt instead of this.
28 #define CR_AF_MAKE_FILE_COPY 0
30 __forceinline HMODULE
get_my_module_handle(void)
32 static int s_module_marker
= 0;
33 MEMORY_BASIC_INFORMATION memory_basic_information
;
34 if (!VirtualQuery(&s_module_marker
, &memory_basic_information
, sizeof(memory_basic_information
)))
38 return (HMODULE
)memory_basic_information
.AllocationBase
;
43 * helper class for the DoctorDumpSDK
49 : m_InitCrashHandler(nullptr)
50 , m_SendReport(nullptr)
51 , m_IsReadyToExit(nullptr)
52 , m_AddUserInfoToReport(nullptr)
53 , m_AddFileToReport(nullptr)
54 , m_RemoveFileFromReport(nullptr)
55 , m_GetVersionFromApp(nullptr)
56 , m_GetVersionFromFile(nullptr)
66 // If crash has happen not in main thread we should wait here until report will be sent
67 // or else program will be terminated after return from main() and report sending will be halted.
68 while (!m_IsReadyToExit())
71 if (m_bSkipAssertsAdded
)
72 RemoveVectoredExceptionHandler(SkipAsserts
);
76 static CCrashReport
& Instance()
78 static CCrashReport instance
;
82 int Uninstall(void) { return FALSE
; }
83 int AddFile2(LPCTSTR pszFile
,LPCTSTR pszDestFile
,LPCTSTR
/*pszDesc*/,DWORD
/*dwFlags*/)
85 return AddFileToReport(pszFile
, pszDestFile
) ? 1 : 0;
88 //! Checks that crash handling was enabled.
89 //! \return Return \b true if crash handling was enabled.
90 bool IsCrashHandlingEnabled() const
95 //! Initializes crash handler.
96 //! \note You may call this function multiple times if some data has changed.
97 //! \return Return \b true if crash handling was enabled.
98 bool InitCrashHandler(
99 ApplicationInfo
* applicationInfo
, //!< [in] Pointer to the ApplicationInfo structure that identifies your application.
100 HandlerSettings
* handlerSettings
, //!< [in] Pointer to the HandlerSettings structure that customizes crash handling behavior. This parameter can be \b NULL.
101 BOOL ownProcess
= TRUE
//!< [in] If you own the process your code running in set this option to \b TRUE. If don't (for example you write
102 //!< a plugin to some external application) set this option to \b FALSE. In that case you need to explicitly
103 //!< catch exceptions. See \ref SendReport for more information.
106 if (!m_InitCrashHandler
)
109 m_bWorking
= m_InitCrashHandler(applicationInfo
, handlerSettings
, ownProcess
) != FALSE
;
114 //! \note This function is experimental and may not be available and may not be support by Doctor Dump in the future.
115 //! You may set custom information for your possible report.
116 //! This text will be available on Doctor Dump dumps page.
117 //! The text should not contain private information.
118 //! \return If the function succeeds, the return value is \b true.
120 LPCWSTR text
//!< [in] custom info for the report. The text will be cut to 100 characters.
123 if (!m_SetCustomInfo
)
125 m_SetCustomInfo(text
);
129 //! You may add any key/value pair to crash report.
130 //! \return If the function succeeds, the return value is \b true.
131 //! \note This function is thread safe.
132 bool AddUserInfoToReport(
133 LPCWSTR key
, //!< [in] key string that will be added to the report.
134 LPCWSTR value
//!< [in] value for the key.
137 if (!m_AddUserInfoToReport
)
139 m_AddUserInfoToReport(key
, value
);
143 //! You may remove any key that was added previously to crash report by \a AddUserInfoToReport.
144 //! \return If the function succeeds, the return value is \b true.
145 //! \note This function is thread safe.
146 bool RemoveUserInfoFromReport(
147 LPCWSTR key
//!< [in] key string that will be removed from the report.
150 if (!m_RemoveUserInfoFromReport
)
152 m_RemoveUserInfoFromReport(key
);
156 //! You may add any file to crash report. This file will be read when crash appears and will be sent within the report.
157 //! Multiple files may be added. Filename of the file in the report may be changed to any name.
158 //! \return If the function succeeds, the return value is \b true.
159 //! \note This function is thread safe.
160 bool AddFileToReport(
161 LPCWSTR path
, //!< [in] Path to the file, that will be added to the report.
162 LPCWSTR reportFileName
/* = NULL */ //!< [in] Filename that will be used in report for this file. If parameter is \b NULL, original name from path will be used.
165 if (!m_AddFileToReport
)
167 m_AddFileToReport(path
, reportFileName
);
171 //! Remove from report the file that was registered earlier to be sent within report.
172 //! \return If the function succeeds, the return value is \b true.
173 //! \note This function is thread safe.
174 bool RemoveFileFromReport(
175 LPCWSTR path
//!< [in] Path to the file, that will be removed from the report.
178 if (!m_RemoveFileFromReport
)
180 m_RemoveFileFromReport(path
);
184 //! Fills version field (V) of ApplicationInfo with product version
185 //! found in the executable file of the current process.
186 //! \return If the function succeeds, the return value is \b true.
187 bool GetVersionFromApp(
188 ApplicationInfo
* appInfo
//!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
191 if (!m_GetVersionFromApp
)
193 return m_GetVersionFromApp(appInfo
) != FALSE
;
196 //! Fill version field (V) of ApplicationInfo with product version found in the file specified.
197 //! \return If the function succeeds, the return value is \b true.
198 bool GetVersionFromFile(
199 LPCWSTR path
, //!< [in] Path to the file product version will be extracted from.
200 ApplicationInfo
* appInfo
//!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
203 if (!m_GetVersionFromFile
)
205 return m_GetVersionFromFile(path
, appInfo
) != FALSE
;
208 //! If you do not own the process your code running in (for example you write a plugin to some
209 //! external application) you need to properly initialize CrashHandler using \b ownProcess option.
210 //! Also you need to explicitly catch all exceptions in all entry points to your code and in all
211 //! threads you create. To do so use this construction:
213 //! bool SomeEntryPoint(PARAM p)
217 //! return YouCode(p);
219 //! __except (CrashHandler::SendReport(GetExceptionInformation()))
221 //! ::ExitProcess(0); // It is better to stop the process here or else corrupted data may incomprehensibly crash it later.
227 EXCEPTION_POINTERS
* exceptionPointers
//!< [in] Pointer to EXCEPTION_POINTERS structure. You should get it using GetExceptionInformation()
228 //!< function inside __except keyword.
232 return EXCEPTION_CONTINUE_SEARCH
;
233 // There is no crash handler but asserts should continue anyway
234 if (exceptionPointers
->ExceptionRecord
->ExceptionCode
== ExceptionAssertionViolated
)
235 return EXCEPTION_CONTINUE_EXECUTION
;
236 return m_SendReport(exceptionPointers
);
239 //! To send a report about violated assertion you can throw exception with this exception code
240 //! using: \code RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL); \endcode
241 //! Execution will continue after report will be sent (EXCEPTION_CONTINUE_EXECUTION would be used).
242 //! You may pass grouping string as first parameter (see \a SkipDoctorDump_SendAssertionViolated).
243 //! \note If you called CrashHandler constructor and crshhndl.dll was missing you still may using this exception.
244 //! It will be caught, ignored and execution will continue. \ref SendReport function also works safely
245 //! when crshhndl.dll was missing.
246 static const DWORD ExceptionAssertionViolated
= ((DWORD
)0xCCE17000);
248 //! Sends assertion violation report from this point and continue execution.
249 //! \sa ExceptionAssertionViolated
250 //! \note Functions containing "SkipDoctorDump" will be ignored in stack parsing.
251 void SkipDoctorDump_SendAssertionViolated(
252 LPCSTR dumpGroup
= NULL
//!< [in] All dumps with that group will be separated from dumps with same stack but another group. Set parameter to \b NULL if no grouping is required.
258 ::RaiseException(CrashHandler::ExceptionAssertionViolated
, 0, 1, reinterpret_cast<ULONG_PTR
*>(&dumpGroup
));
260 ::RaiseException(CrashHandler::ExceptionAssertionViolated
, 0, 0, NULL
);
264 bool LoadDll(LPCWSTR crashHandlerPath
= NULL
) throw()
268 m_bSkipAssertsAdded
= false;
269 m_InitCrashHandler
= NULL
;
271 m_IsReadyToExit
= NULL
;
272 m_SetCustomInfo
= NULL
;
273 m_AddUserInfoToReport
= NULL
;
274 m_RemoveUserInfoFromReport
= NULL
;
275 m_AddFileToReport
= NULL
;
276 m_RemoveFileFromReport
= NULL
;
277 m_GetVersionFromApp
= NULL
;
278 m_GetVersionFromFile
= NULL
;
280 // hCrashHandlerDll should not be unloaded, crash may appear even after return from main().
281 // So hCrashHandlerDll is not saved after construction.
283 IsWow64Process(GetCurrentProcess(), &bIsWow
);
284 HMODULE hCrashHandlerDll
= NULL
;
287 if (crashHandlerPath
== nullptr)
289 HMODULE hDll
= get_my_module_handle();
290 wchar_t modpath
[1024] = { 0 };
291 if (GetModuleFileName(hDll
, modpath
, _countof(modpath
)))
293 wchar_t * dirpoint
= wcsrchr(modpath
, '\\');
297 wcscat_s(modpath
, L
"\\crshhndl.dll");
298 hCrashHandlerDll
= ::LoadLibraryW(modpath
);
301 hCrashHandlerDll
= ::LoadLibraryW(L
"crshhndl.dll");
304 hCrashHandlerDll
= ::LoadLibraryW(L
"crshhndl.dll");
307 hCrashHandlerDll
= ::LoadLibraryW(crashHandlerPath
);
309 if (hCrashHandlerDll
!= NULL
)
311 m_InitCrashHandler
= (pfnInitCrashHandler
) GetProcAddress(hCrashHandlerDll
, "InitCrashHandler");
312 m_SendReport
= (pfnSendReport
) GetProcAddress(hCrashHandlerDll
, "SendReport");
313 m_IsReadyToExit
= (pfnIsReadyToExit
) GetProcAddress(hCrashHandlerDll
, "IsReadyToExit");
314 m_SetCustomInfo
= (pfnSetCustomInfo
) GetProcAddress(hCrashHandlerDll
, "SetCustomInfo");
315 m_AddUserInfoToReport
= (pfnAddUserInfoToReport
) GetProcAddress(hCrashHandlerDll
, "AddUserInfoToReport");
316 m_RemoveUserInfoFromReport
= (pfnRemoveUserInfoFromReport
) GetProcAddress(hCrashHandlerDll
, "RemoveUserInfoFromReport");
317 m_AddFileToReport
= (pfnAddFileToReport
) GetProcAddress(hCrashHandlerDll
, "AddFileToReport");
318 m_RemoveFileFromReport
= (pfnRemoveFileFromReport
) GetProcAddress(hCrashHandlerDll
, "RemoveFileFromReport");
319 m_GetVersionFromApp
= (pfnGetVersionFromApp
) GetProcAddress(hCrashHandlerDll
, "GetVersionFromApp");
320 m_GetVersionFromFile
= (pfnGetVersionFromFile
) GetProcAddress(hCrashHandlerDll
, "GetVersionFromFile");
322 m_bLoaded
= m_InitCrashHandler
326 && m_AddUserInfoToReport
327 && m_RemoveUserInfoFromReport
329 && m_RemoveFileFromReport
330 && m_GetVersionFromApp
331 && m_GetVersionFromFile
;
334 #if 0 // TGit don't use it
335 #if _WIN32_WINNT >= 0x0501 /*_WIN32_WINNT_WINXP*/
336 // if no crash processing was started, we need to ignore ExceptionAssertionViolated exceptions.
339 ::AddVectoredExceptionHandler(TRUE
, SkipAsserts
);
340 m_bSkipAssertsAdded
= true;
348 static LONG CALLBACK
SkipAsserts(EXCEPTION_POINTERS
* pExceptionInfo
)
350 if (pExceptionInfo
->ExceptionRecord
->ExceptionCode
== ExceptionAssertionViolated
)
351 return EXCEPTION_CONTINUE_EXECUTION
;
352 return EXCEPTION_CONTINUE_SEARCH
;
357 bool m_bSkipAssertsAdded
;
359 typedef BOOL (*pfnInitCrashHandler
)(ApplicationInfo
* applicationInfo
, HandlerSettings
* handlerSettings
, BOOL ownProcess
);
360 typedef LONG (*pfnSendReport
)(EXCEPTION_POINTERS
* exceptionPointers
);
361 typedef BOOL (*pfnIsReadyToExit
)();
362 typedef void (*pfnSetCustomInfo
)(LPCWSTR text
);
363 typedef void (*pfnAddUserInfoToReport
)(LPCWSTR key
, LPCWSTR value
);
364 typedef void (*pfnRemoveUserInfoFromReport
)(LPCWSTR key
);
365 typedef void (*pfnAddFileToReport
)(LPCWSTR path
, LPCWSTR reportFileName
/* = NULL */);
366 typedef void (*pfnRemoveFileFromReport
)(LPCWSTR path
);
367 typedef BOOL (*pfnGetVersionFromApp
)(ApplicationInfo
* appInfo
);
368 typedef BOOL (*pfnGetVersionFromFile
)(LPCWSTR path
, ApplicationInfo
* appInfo
);
370 pfnInitCrashHandler m_InitCrashHandler
;
371 pfnSendReport m_SendReport
;
372 pfnIsReadyToExit m_IsReadyToExit
;
373 pfnSetCustomInfo m_SetCustomInfo
;
374 pfnAddUserInfoToReport m_AddUserInfoToReport
;
375 pfnRemoveUserInfoFromReport m_RemoveUserInfoFromReport
;
376 pfnAddFileToReport m_AddFileToReport
;
377 pfnRemoveFileFromReport m_RemoveFileFromReport
;
378 pfnGetVersionFromApp m_GetVersionFromApp
;
379 pfnGetVersionFromFile m_GetVersionFromFile
;
382 class CCrashReportTGit
386 //! Installs exception handlers to the caller process
387 CCrashReportTGit(LPCTSTR appname
, USHORT versionMajor
, USHORT versionMinor
, USHORT versionMicro
, USHORT versionBuild
, const char* /*buildDate*/, bool bOwnProcess
= true)
388 : m_nInstallStatus(0)
390 ApplicationInfo appInfo
= { 0 };
391 appInfo
.ApplicationInfoSize
= sizeof(ApplicationInfo
);
392 appInfo
.ApplicationGUID
= "7fbde3fc-94e9-408b-b5c8-62bd4e203570";
393 appInfo
.Prefix
= "tgit";
394 appInfo
.AppName
= appname
;
395 appInfo
.Company
= L
"TortoiseGit";
398 appInfo
.V
[0] = versionMajor
;
399 appInfo
.V
[1] = versionMinor
;
400 appInfo
.V
[2] = versionMicro
;
401 appInfo
.V
[3] = versionBuild
;
403 HandlerSettings handlerSettings
= { 0 };
404 handlerSettings
.HandlerSettingsSize
= sizeof(handlerSettings
);
405 handlerSettings
.LeaveDumpFilesInTempFolder
= FALSE
;
406 handlerSettings
.UseWER
= FALSE
;
407 handlerSettings
.OpenProblemInBrowser
= TRUE
;
409 CCrashReport::Instance().InitCrashHandler(&appInfo
, &handlerSettings
, bOwnProcess
);
413 //! Deinstalls exception handlers from the caller process
416 CCrashReport::Instance().Uninstall();
420 int m_nInstallStatus
;