Do not include version.h in CrashReport.h in order to speed up building
[TortoiseGit.git] / src / Utils / CrashReport.h
blob2a4d1f23df5e90553c4b23cd85c88a0b5e4b45ad
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (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 General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #pragma once
20 #include "../../ext/CrashServer/CrashHandler/CrashHandler/CrashHandler.h"
21 #include <time.h>
22 #include <string>
23 #include <tchar.h>
25 // dummy define, needed only when we use crashrpt instead of this.
26 #define CR_AF_MAKE_FILE_COPY 0
28 /**
29 * \ingroup Utils
30 * helper class for the CrashServerSDK
32 class CCrashReport
34 private:
35 CCrashReport(void)
37 LoadDll();
40 ~CCrashReport(void)
44 public:
45 static CCrashReport& Instance()
47 static CCrashReport instance;
48 return instance;
51 int Uninstall(void) { return FALSE; }
52 int AddFile2(LPCTSTR pszFile,LPCTSTR pszDestFile,LPCTSTR /*pszDesc*/,DWORD /*dwFlags*/)
54 return AddFileToReport(pszFile, pszDestFile) ? 1 : 0;
58 //! Initializes crash handler.
59 //! \note You may call this function multiple times if some data has changed.
60 //! \return Return \b true if crash handling was enabled.
61 bool InitCrashHandler(
62 ApplicationInfo* applicationInfo, //!< [in] Pointer to the ApplicationInfo structure that identifies your application.
63 HandlerSettings* handlerSettings, //!< [in] Pointer to the HandlerSettings structure that customizes crash handling behavior. This paramenter can be \b NULL.
64 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
65 //!< a plugin to some external application) set this option to \b FALSE. In that case you need to explicitly
66 //!< catch exceptions. See \ref SendReport for more information.
67 ) throw()
69 if (!m_InitCrashHandler)
70 return false;
72 return m_InitCrashHandler(applicationInfo, handlerSettings, ownProcess) != FALSE;
75 //! You may add any key/value pair to crash report.
76 //! \return If the function succeeds, the return value is \b true.
77 bool AddUserInfoToReport(
78 LPCWSTR key, //!< [in] key string that will be added to the report.
79 LPCWSTR value //!< [in] value for the key.
80 ) throw()
82 if (!m_AddUserInfoToReport)
83 return false;
84 m_AddUserInfoToReport(key, value);
85 return true;
88 //! You may add any file to crash report. This file will be read when crash appears and will be sent within the report.
89 //! Multiple files may be added. Filename of the file in the report may be changed to any name.
90 //! \return If the function succeeds, the return value is \b true.
91 bool AddFileToReport(
92 LPCWSTR path, //!< [in] Path to the file, that will be added to the report.
93 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.
94 ) throw()
96 if (!m_AddFileToReport)
97 return false;
98 m_AddFileToReport(path, reportFileName);
99 return true;
102 //! Remove from report the file that was registered earlier to be sent within report.
103 //! \return If the function succeeds, the return value is \b true.
104 bool RemoveFileFromReport(
105 LPCWSTR path //!< [in] Path to the file, that will be removed from the report.
106 ) throw()
108 if (!m_RemoveFileFromReport)
109 return false;
110 m_RemoveFileFromReport(path);
111 return true;
114 //! Fills version field (V) of ApplicationInfo with product version
115 //! found in the executable file of the current process.
116 //! \return If the function succeeds, the return value is \b true.
117 bool GetVersionFromApp(
118 ApplicationInfo* appInfo //!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
119 ) throw()
121 if (!m_GetVersionFromApp)
122 return false;
123 return m_GetVersionFromApp(appInfo) != FALSE;
126 //! Fill version field (V) of ApplicationInfo with product version found in the file specified.
127 //! \return If the function succeeds, the return value is \b true.
128 bool GetVersionFromFile(
129 LPCWSTR path, //!< [in] Path to the file product version will be extracted from.
130 ApplicationInfo* appInfo //!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
131 ) throw()
133 if (!m_GetVersionFromFile)
134 return false;
135 return m_GetVersionFromFile(path, appInfo) != FALSE;
138 //! If you do not own the process your code running in (for example you write a plugin to some
139 //! external application) you need to properly initialize CrashHandler using \b ownProcess option.
140 //! Also you need to explicitly catch all exceptions in all entry points to your code and in all
141 //! threads you create. To do so use this construction:
142 //! \code
143 //! bool SomeEntryPoint(PARAM p)
144 //! {
145 //! __try
146 //! {
147 //! return YouCode(p);
148 //! }
149 //! __except (CrashHandler::SendReport(GetExceptionInformation()))
150 //! {
151 //! ::ExitProcess(0); // It is better to stop the process here or else corrupted data may incomprehensibly crash it later.
152 //! return false;
153 //! }
154 //! }
155 //! \endcode
156 LONG SendReport(
157 EXCEPTION_POINTERS* exceptionPointers //!< [in] Pointer to EXCEPTION_POINTERS structure. You should get it using GetExceptionInformation()
158 //!< function inside __except keyword.
161 if (!m_SendReport)
162 return EXCEPTION_CONTINUE_SEARCH;
163 // There is no crash handler but asserts should continue anyway
164 if (exceptionPointers->ExceptionRecord->ExceptionCode == ExceptionAssertionViolated)
165 return EXCEPTION_CONTINUE_EXECUTION;
166 return m_SendReport(exceptionPointers);
169 //! To send a report about violated assertion you can throw exception with this exception code
170 //! using: \code RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL); \endcode
171 //! Execution will continue after report will be sent (EXCEPTION_CONTINUE_EXECUTION would be used).
172 //! \note If you called CrashHandler constructor and crshhdnl.dll was missing you still may using this exception.
173 //! It will be catched, ignored and execution will continue. \ref SendReport function also works safely
174 //! when crshhdnl.dll was missing.
175 static const DWORD ExceptionAssertionViolated = ((DWORD)0xCCE17000);
177 //! Sends assertion violation report from this point and continue execution.
178 //! \sa ExceptionAssertionViolated
179 //! \note Functions prefixed with "CrashServer_" will be ignored in stack parsing.
180 void CrashServer_SendAssertionViolated()
182 if (!m_InitCrashHandler)
183 return;
184 ::RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL);
187 private:
188 bool LoadDll() throw()
190 bool result = false;
192 // hCrshhndlDll should not be unloaded, crash may appear even after return from main().
193 // So hCrshhndlDll is not saved after construction.
194 HMODULE hCrshhndlDll = ::LoadLibraryW(L"crshhndl.dll");
195 if (hCrshhndlDll != NULL)
197 m_InitCrashHandler = (pfnInitCrashHandler) GetProcAddress(hCrshhndlDll, "InitCrashHandler");
198 m_SendReport = (pfnSendReport) GetProcAddress(hCrshhndlDll, "SendReport");
199 m_IsReadyToExit = (pfnIsReadyToExit) GetProcAddress(hCrshhndlDll, "IsReadyToExit");
200 m_AddUserInfoToReport = (pfnAddUserInfoToReport) GetProcAddress(hCrshhndlDll, "AddUserInfoToReport");
201 m_AddFileToReport = (pfnAddFileToReport) GetProcAddress(hCrshhndlDll, "AddFileToReport");
202 m_RemoveFileFromReport = (pfnRemoveFileFromReport) GetProcAddress(hCrshhndlDll, "RemoveFileFromReport");
203 m_GetVersionFromApp = (pfnGetVersionFromApp) GetProcAddress(hCrshhndlDll, "GetVersionFromApp");
204 m_GetVersionFromFile = (pfnGetVersionFromFile) GetProcAddress(hCrshhndlDll, "GetVersionFromFile");
206 result = m_InitCrashHandler
207 && m_SendReport
208 && m_IsReadyToExit
209 && m_AddUserInfoToReport
210 && m_AddFileToReport
211 && m_RemoveFileFromReport
212 && m_GetVersionFromApp
213 && m_GetVersionFromFile;
216 return result;
219 static LONG CALLBACK SkipAsserts(EXCEPTION_POINTERS* pExceptionInfo)
221 if (pExceptionInfo->ExceptionRecord->ExceptionCode == ExceptionAssertionViolated)
222 return EXCEPTION_CONTINUE_EXECUTION;
223 return EXCEPTION_CONTINUE_SEARCH;
226 typedef BOOL (*pfnInitCrashHandler)(ApplicationInfo* applicationInfo, HandlerSettings* handlerSettings, BOOL ownProcess);
227 typedef LONG (*pfnSendReport)(EXCEPTION_POINTERS* exceptionPointers);
228 typedef BOOL (*pfnIsReadyToExit)();
229 typedef void (*pfnAddUserInfoToReport)(LPCWSTR key, LPCWSTR value);
230 typedef void (*pfnAddFileToReport)(LPCWSTR path, LPCWSTR reportFileName /* = NULL */);
231 typedef void (*pfnRemoveFileFromReport)(LPCWSTR path);
232 typedef BOOL (*pfnGetVersionFromApp)(ApplicationInfo* appInfo);
233 typedef BOOL (*pfnGetVersionFromFile)(LPCWSTR path, ApplicationInfo* appInfo);
235 pfnInitCrashHandler m_InitCrashHandler;
236 pfnSendReport m_SendReport;
237 pfnIsReadyToExit m_IsReadyToExit;
238 pfnAddUserInfoToReport m_AddUserInfoToReport;
239 pfnAddFileToReport m_AddFileToReport;
240 pfnRemoveFileFromReport m_RemoveFileFromReport;
241 pfnGetVersionFromApp m_GetVersionFromApp;
242 pfnGetVersionFromFile m_GetVersionFromFile;
246 class CCrashReportTGit
248 public:
250 //! Installs exception handlers to the caller process
251 CCrashReportTGit(LPCTSTR appname, USHORT versionMajor, USHORT versionMinor, USHORT versionMicro, USHORT versionBuild, bool bOwnProcess = true)
252 : m_nInstallStatus(0)
254 char s_month[6];
255 int month, day, year;
256 struct tm t = {0};
257 static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
258 sscanf_s(__DATE__, "%s %d %d", s_month, _countof(s_month)-1, &day, &year);
259 month = (int)((strstr(month_names, s_month)-month_names))/3;
261 t.tm_mon = month;
262 t.tm_mday = day;
263 t.tm_year = year - 1900;
264 t.tm_isdst = -1;
265 __time64_t compiletime = _mktime64(&t);
267 __time64_t now;
268 _time64(&now);
270 if ((now - compiletime)<(60*60*24*31*4))
272 ApplicationInfo appInfo;
273 memset(&appInfo, 0, sizeof(appInfo));
274 appInfo.ApplicationInfoSize = sizeof(ApplicationInfo);
275 appInfo.ApplicationGUID = "7fbde3fc-94e9-408b-b5c8-62bd4e203570";
276 appInfo.Prefix = "tgit";
277 appInfo.AppName = appname;
278 appInfo.Company = L"TortoiseGit";
280 appInfo.Hotfix = 0;
281 appInfo.V[0] = versionMajor;
282 appInfo.V[1] = versionMinor;
283 appInfo.V[2] = versionMicro;
284 appInfo.V[3] = versionBuild;
286 HandlerSettings handlerSettings;
287 memset(&handlerSettings, 0, sizeof(handlerSettings));
288 handlerSettings.HandlerSettingsSize = sizeof(handlerSettings);
289 handlerSettings.LeaveDumpFilesInTempFolder = FALSE;
290 handlerSettings.UseWER = FALSE;
291 handlerSettings.OpenProblemInBrowser = TRUE;
292 handlerSettings.SubmitterID = 0;
294 CCrashReport::Instance().InitCrashHandler(&appInfo, &handlerSettings, bOwnProcess);
299 //! Deinstalls exception handlers from the caller process
300 ~CCrashReportTGit()
302 CCrashReport::Instance().Uninstall();
305 //! Install status
306 int m_nInstallStatus;