Use MassiveGitTask for index manipulation
[TortoiseGit.git] / src / Utils / CrashReport.h
blob398a16c1c8a8e89c25de3247da3f173f2dc58911
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013 - TortoiseGit
4 // Copyright (C) 2012 - 2013 - 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.
20 #pragma once
21 #include "../../ext/CrashServer/CrashHandler/CrashHandler/CrashHandler.h"
22 #include <time.h>
23 #include <string>
24 #include <tchar.h>
26 // dummy define, needed only when we use crashrpt instead of this.
27 #define CR_AF_MAKE_FILE_COPY 0
29 /**
30 * \ingroup Utils
31 * helper class for the CrashServerSDK
33 class CCrashReport
35 private:
36 CCrashReport(void)
38 LoadDll();
41 ~CCrashReport(void)
45 public:
46 static CCrashReport& Instance()
48 static CCrashReport instance;
49 return instance;
52 int Uninstall(void) { return FALSE; }
53 int AddFile2(LPCTSTR pszFile,LPCTSTR pszDestFile,LPCTSTR /*pszDesc*/,DWORD /*dwFlags*/)
55 return AddFileToReport(pszFile, pszDestFile) ? 1 : 0;
59 //! Initializes crash handler.
60 //! \note You may call this function multiple times if some data has changed.
61 //! \return Return \b true if crash handling was enabled.
62 bool InitCrashHandler(
63 ApplicationInfo* applicationInfo, //!< [in] Pointer to the ApplicationInfo structure that identifies your application.
64 HandlerSettings* handlerSettings, //!< [in] Pointer to the HandlerSettings structure that customizes crash handling behavior. This paramenter can be \b NULL.
65 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
66 //!< a plugin to some external application) set this option to \b FALSE. In that case you need to explicitly
67 //!< catch exceptions. See \ref SendReport for more information.
68 ) throw()
70 if (!m_InitCrashHandler)
71 return false;
73 return m_InitCrashHandler(applicationInfo, handlerSettings, ownProcess) != FALSE;
76 //! You may add any key/value pair to crash report.
77 //! \return If the function succeeds, the return value is \b true.
78 bool AddUserInfoToReport(
79 LPCWSTR key, //!< [in] key string that will be added to the report.
80 LPCWSTR value //!< [in] value for the key.
81 ) throw()
83 if (!m_AddUserInfoToReport)
84 return false;
85 m_AddUserInfoToReport(key, value);
86 return true;
89 //! You may add any file to crash report. This file will be read when crash appears and will be sent within the report.
90 //! Multiple files may be added. Filename of the file in the report may be changed to any name.
91 //! \return If the function succeeds, the return value is \b true.
92 bool AddFileToReport(
93 LPCWSTR path, //!< [in] Path to the file, that will be added to the report.
94 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.
95 ) throw()
97 if (!m_AddFileToReport)
98 return false;
99 m_AddFileToReport(path, reportFileName);
100 return true;
103 //! Remove from report the file that was registered earlier to be sent within report.
104 //! \return If the function succeeds, the return value is \b true.
105 bool RemoveFileFromReport(
106 LPCWSTR path //!< [in] Path to the file, that will be removed from the report.
107 ) throw()
109 if (!m_RemoveFileFromReport)
110 return false;
111 m_RemoveFileFromReport(path);
112 return true;
115 //! Fills version field (V) of ApplicationInfo with product version
116 //! found in the executable file of the current process.
117 //! \return If the function succeeds, the return value is \b true.
118 bool GetVersionFromApp(
119 ApplicationInfo* appInfo //!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
120 ) throw()
122 if (!m_GetVersionFromApp)
123 return false;
124 return m_GetVersionFromApp(appInfo) != FALSE;
127 //! Fill version field (V) of ApplicationInfo with product version found in the file specified.
128 //! \return If the function succeeds, the return value is \b true.
129 bool GetVersionFromFile(
130 LPCWSTR path, //!< [in] Path to the file product version will be extracted from.
131 ApplicationInfo* appInfo //!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
132 ) throw()
134 if (!m_GetVersionFromFile)
135 return false;
136 return m_GetVersionFromFile(path, appInfo) != FALSE;
139 //! If you do not own the process your code running in (for example you write a plugin to some
140 //! external application) you need to properly initialize CrashHandler using \b ownProcess option.
141 //! Also you need to explicitly catch all exceptions in all entry points to your code and in all
142 //! threads you create. To do so use this construction:
143 //! \code
144 //! bool SomeEntryPoint(PARAM p)
145 //! {
146 //! __try
147 //! {
148 //! return YouCode(p);
149 //! }
150 //! __except (CrashHandler::SendReport(GetExceptionInformation()))
151 //! {
152 //! ::ExitProcess(0); // It is better to stop the process here or else corrupted data may incomprehensibly crash it later.
153 //! return false;
154 //! }
155 //! }
156 //! \endcode
157 LONG SendReport(
158 EXCEPTION_POINTERS* exceptionPointers //!< [in] Pointer to EXCEPTION_POINTERS structure. You should get it using GetExceptionInformation()
159 //!< function inside __except keyword.
162 if (!m_SendReport)
163 return EXCEPTION_CONTINUE_SEARCH;
164 // There is no crash handler but asserts should continue anyway
165 if (exceptionPointers->ExceptionRecord->ExceptionCode == ExceptionAssertionViolated)
166 return EXCEPTION_CONTINUE_EXECUTION;
167 return m_SendReport(exceptionPointers);
170 //! To send a report about violated assertion you can throw exception with this exception code
171 //! using: \code RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL); \endcode
172 //! Execution will continue after report will be sent (EXCEPTION_CONTINUE_EXECUTION would be used).
173 //! \note If you called CrashHandler constructor and crshhdnl.dll was missing you still may using this exception.
174 //! It will be catched, ignored and execution will continue. \ref SendReport function also works safely
175 //! when crshhdnl.dll was missing.
176 static const DWORD ExceptionAssertionViolated = ((DWORD)0xCCE17000);
178 //! Sends assertion violation report from this point and continue execution.
179 //! \sa ExceptionAssertionViolated
180 //! \note Functions prefixed with "CrashServer_" will be ignored in stack parsing.
181 void CrashServer_SendAssertionViolated() const
183 if (!m_InitCrashHandler)
184 return;
185 ::RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL);
188 private:
189 bool LoadDll() throw()
191 bool result = false;
193 // hCrshhndlDll should not be unloaded, crash may appear even after return from main().
194 // So hCrshhndlDll is not saved after construction.
195 HMODULE hCrshhndlDll = ::LoadLibraryW(L"crshhndl.dll");
196 if (hCrshhndlDll != NULL)
198 m_InitCrashHandler = (pfnInitCrashHandler) GetProcAddress(hCrshhndlDll, "InitCrashHandler");
199 m_SendReport = (pfnSendReport) GetProcAddress(hCrshhndlDll, "SendReport");
200 m_IsReadyToExit = (pfnIsReadyToExit) GetProcAddress(hCrshhndlDll, "IsReadyToExit");
201 m_AddUserInfoToReport = (pfnAddUserInfoToReport) GetProcAddress(hCrshhndlDll, "AddUserInfoToReport");
202 m_AddFileToReport = (pfnAddFileToReport) GetProcAddress(hCrshhndlDll, "AddFileToReport");
203 m_RemoveFileFromReport = (pfnRemoveFileFromReport) GetProcAddress(hCrshhndlDll, "RemoveFileFromReport");
204 m_GetVersionFromApp = (pfnGetVersionFromApp) GetProcAddress(hCrshhndlDll, "GetVersionFromApp");
205 m_GetVersionFromFile = (pfnGetVersionFromFile) GetProcAddress(hCrshhndlDll, "GetVersionFromFile");
207 result = m_InitCrashHandler
208 && m_SendReport
209 && m_IsReadyToExit
210 && m_AddUserInfoToReport
211 && m_AddFileToReport
212 && m_RemoveFileFromReport
213 && m_GetVersionFromApp
214 && m_GetVersionFromFile;
217 return result;
220 static LONG CALLBACK SkipAsserts(EXCEPTION_POINTERS* pExceptionInfo)
222 if (pExceptionInfo->ExceptionRecord->ExceptionCode == ExceptionAssertionViolated)
223 return EXCEPTION_CONTINUE_EXECUTION;
224 return EXCEPTION_CONTINUE_SEARCH;
227 typedef BOOL (*pfnInitCrashHandler)(ApplicationInfo* applicationInfo, HandlerSettings* handlerSettings, BOOL ownProcess);
228 typedef LONG (*pfnSendReport)(EXCEPTION_POINTERS* exceptionPointers);
229 typedef BOOL (*pfnIsReadyToExit)();
230 typedef void (*pfnAddUserInfoToReport)(LPCWSTR key, LPCWSTR value);
231 typedef void (*pfnAddFileToReport)(LPCWSTR path, LPCWSTR reportFileName /* = NULL */);
232 typedef void (*pfnRemoveFileFromReport)(LPCWSTR path);
233 typedef BOOL (*pfnGetVersionFromApp)(ApplicationInfo* appInfo);
234 typedef BOOL (*pfnGetVersionFromFile)(LPCWSTR path, ApplicationInfo* appInfo);
236 pfnInitCrashHandler m_InitCrashHandler;
237 pfnSendReport m_SendReport;
238 pfnIsReadyToExit m_IsReadyToExit;
239 pfnAddUserInfoToReport m_AddUserInfoToReport;
240 pfnAddFileToReport m_AddFileToReport;
241 pfnRemoveFileFromReport m_RemoveFileFromReport;
242 pfnGetVersionFromApp m_GetVersionFromApp;
243 pfnGetVersionFromFile m_GetVersionFromFile;
247 class CCrashReportTGit
249 public:
251 //! Installs exception handlers to the caller process
252 CCrashReportTGit(LPCTSTR appname, USHORT versionMajor, USHORT versionMinor, USHORT versionMicro, USHORT versionBuild, const char * buildDate, bool bOwnProcess = true)
253 : m_nInstallStatus(0)
255 char s_month[6];
256 int month, day, year;
257 struct tm t = {0};
258 static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
259 sscanf_s(buildDate, "%s %d %d", s_month, _countof(s_month) - 1, &day, &year);
260 month = (int)((strstr(month_names, s_month)-month_names))/3;
262 t.tm_mon = month;
263 t.tm_mday = day;
264 t.tm_year = year - 1900;
265 t.tm_isdst = -1;
266 __time64_t compiletime = _mktime64(&t);
268 __time64_t now;
269 _time64(&now);
271 if ((now - compiletime)<(60*60*24*31*4))
273 ApplicationInfo appInfo;
274 memset(&appInfo, 0, sizeof(appInfo));
275 appInfo.ApplicationInfoSize = sizeof(ApplicationInfo);
276 appInfo.ApplicationGUID = "7fbde3fc-94e9-408b-b5c8-62bd4e203570";
277 appInfo.Prefix = "tgit";
278 appInfo.AppName = appname;
279 appInfo.Company = L"TortoiseGit";
281 appInfo.Hotfix = 0;
282 appInfo.V[0] = versionMajor;
283 appInfo.V[1] = versionMinor;
284 appInfo.V[2] = versionMicro;
285 appInfo.V[3] = versionBuild;
287 HandlerSettings handlerSettings;
288 memset(&handlerSettings, 0, sizeof(handlerSettings));
289 handlerSettings.HandlerSettingsSize = sizeof(handlerSettings);
290 handlerSettings.LeaveDumpFilesInTempFolder = FALSE;
291 handlerSettings.UseWER = FALSE;
292 handlerSettings.OpenProblemInBrowser = TRUE;
293 handlerSettings.SubmitterID = 0;
295 CCrashReport::Instance().InitCrashHandler(&appInfo, &handlerSettings, bOwnProcess);
300 //! Deinstalls exception handlers from the caller process
301 ~CCrashReportTGit()
303 CCrashReport::Instance().Uninstall();
306 //! Install status
307 int m_nInstallStatus;