Otclient  14/8/2020
win32crashhandler.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #if defined(WIN32) && defined(CRASH_HANDLER)
24 
25 #include "crashhandler.h"
26 #include <framework/global.h>
28 
29 #include <winsock2.h>
30 #include <windows.h>
31 #include <process.h>
32 
33 #ifdef _MSC_VER
34 
35 #pragma warning (push)
36 #pragma warning (disable:4091) // warning C4091: 'typedef ': ignored on left of '' when no variable is declared
37 #include <imagehlp.h>
38 #pragma warning (pop)
39 
40 #else
41 
42 #include <imagehlp.h>
43 
44 #endif
45 
46 const char *getExceptionName(DWORD exceptionCode)
47 {
48  switch (exceptionCode) {
49  case EXCEPTION_ACCESS_VIOLATION: return "Access violation";
50  case EXCEPTION_DATATYPE_MISALIGNMENT: return "Datatype misalignment";
51  case EXCEPTION_BREAKPOINT: return "Breakpoint";
52  case EXCEPTION_SINGLE_STEP: return "Single step";
53  case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array bounds exceeded";
54  case EXCEPTION_FLT_DENORMAL_OPERAND: return "Float denormal operand";
55  case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "Float divide by zero";
56  case EXCEPTION_FLT_INEXACT_RESULT: return "Float inexact result";
57  case EXCEPTION_FLT_INVALID_OPERATION: return "Float invalid operation";
58  case EXCEPTION_FLT_OVERFLOW: return "Float overflow";
59  case EXCEPTION_FLT_STACK_CHECK: return "Float stack check";
60  case EXCEPTION_FLT_UNDERFLOW: return "Float underflow";
61  case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Integer divide by zero";
62  case EXCEPTION_INT_OVERFLOW: return "Integer overflow";
63  case EXCEPTION_PRIV_INSTRUCTION: return "Privileged instruction";
64  case EXCEPTION_IN_PAGE_ERROR: return "In page error";
65  case EXCEPTION_ILLEGAL_INSTRUCTION: return "Illegal instruction";
66  case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable exception";
67  case EXCEPTION_STACK_OVERFLOW: return "Stack overflow";
68  case EXCEPTION_INVALID_DISPOSITION: return "Invalid disposition";
69  case EXCEPTION_GUARD_PAGE: return "Guard page";
70  case EXCEPTION_INVALID_HANDLE: return "Invalid handle";
71  }
72  return "Unknown exception";
73 }
74 
75 void Stacktrace(LPEXCEPTION_POINTERS e, std::stringstream& ss)
76 {
77  PIMAGEHLP_SYMBOL pSym;
78  STACKFRAME sf;
79  HANDLE process, thread;
80  ULONG_PTR dwModBase, Disp;
81  BOOL more = FALSE;
82  DWORD machineType;
83  int count = 0;
84  char modname[MAX_PATH];
85  char symBuffer[sizeof(IMAGEHLP_SYMBOL) + 255];
86 
87  pSym = (PIMAGEHLP_SYMBOL)symBuffer;
88 
89  ZeroMemory(&sf, sizeof(sf));
90 #ifdef _WIN64
91  sf.AddrPC.Offset = e->ContextRecord->Rip;
92  sf.AddrStack.Offset = e->ContextRecord->Rsp;
93  sf.AddrFrame.Offset = e->ContextRecord->Rbp;
94  machineType = IMAGE_FILE_MACHINE_AMD64;
95 #else
96  sf.AddrPC.Offset = e->ContextRecord->Eip;
97  sf.AddrStack.Offset = e->ContextRecord->Esp;
98  sf.AddrFrame.Offset = e->ContextRecord->Ebp;
99  machineType = IMAGE_FILE_MACHINE_I386;
100 #endif
101 
102  sf.AddrPC.Mode = AddrModeFlat;
103  sf.AddrStack.Mode = AddrModeFlat;
104  sf.AddrFrame.Mode = AddrModeFlat;
105 
106  process = GetCurrentProcess();
107  thread = GetCurrentThread();
108 
109  while(1) {
110  more = StackWalk(machineType, process, thread, &sf, e->ContextRecord, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL);
111  if(!more || sf.AddrFrame.Offset == 0)
112  break;
113 
114  dwModBase = SymGetModuleBase(process, sf.AddrPC.Offset);
115  if(dwModBase)
116  GetModuleFileName((HINSTANCE)dwModBase, modname, MAX_PATH);
117  else
118  strcpy(modname, "Unknown");
119 
120  Disp = 0;
121  pSym->SizeOfStruct = sizeof(symBuffer);
122  pSym->MaxNameLength = 254;
123 
124  if(SymGetSymFromAddr(process, sf.AddrPC.Offset, &Disp, pSym))
125  ss << stdext::format(" %d: %s(%s+%#0lx) [0x%016lX]\n", count, modname, pSym->Name, Disp, sf.AddrPC.Offset);
126  else
127  ss << stdext::format(" %d: %s [0x%016lX]\n", count, modname, sf.AddrPC.Offset);
128  ++count;
129  }
130  GlobalFree(pSym);
131 }
132 
133 LONG CALLBACK ExceptionHandler(LPEXCEPTION_POINTERS e)
134 {
135  // generate crash report
136  SymInitialize(GetCurrentProcess(), 0, TRUE);
137  std::stringstream ss;
138  ss << "== application crashed\n";
139  ss << stdext::format("app name: %s\n", g_app.getName());
140  ss << stdext::format("app version: %s\n", g_app.getVersion());
141  ss << stdext::format("build compiler: %s\n", BUILD_COMPILER);
142  ss << stdext::format("build date: %s\n", __DATE__);
143  ss << stdext::format("build type: %s\n", BUILD_TYPE);
144  ss << stdext::format("build revision: %s (%s)\n", BUILD_REVISION, BUILD_COMMIT);
145  ss << stdext::format("crash date: %s\n", stdext::date_time_string());
146  ss << stdext::format("exception: %s (0x%08lx)\n", getExceptionName(e->ExceptionRecord->ExceptionCode), e->ExceptionRecord->ExceptionCode);
147  ss << stdext::format("exception address: 0x%08lx\n", (size_t)e->ExceptionRecord->ExceptionAddress);
148  ss << stdext::format(" backtrace:\n");
149  Stacktrace(e, ss);
150  ss << "\n";
151  SymCleanup(GetCurrentProcess());
152 
153  // print in stdout
154  g_logger.info(ss.str());
155 
156  // write stacktrace to crashreport.log
157  char dir[MAX_PATH];
158  GetCurrentDirectory(sizeof(dir) - 1, dir);
159  std::string fileName = stdext::format("%s\\crashreport.log", dir);
160  std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::app);
161  if(fout.is_open() && fout.good()) {
162  fout << ss.str();
163  fout.close();
164  g_logger.info(stdext::format("Crash report saved to file %s", fileName));
165  } else
166  g_logger.error("Failed to save crash report!");
167 
168  // inform the user
169  std::string msg = stdext::format(
170  "The application has crashed.\n\n"
171  "A crash report has been written to:\n"
172  "%s", fileName.c_str());
173  MessageBox(NULL, msg.c_str(), "Application crashed", 0);
174 
175  // this seems to silently close the application
176  //return EXCEPTION_EXECUTE_HANDLER;
177 
178  // this triggers the microsoft "application has crashed" error dialog
179  return EXCEPTION_CONTINUE_SEARCH;
180 }
181 
182 void installCrashHandler()
183 {
184  SetUnhandledExceptionFilter(ExceptionHandler);
185 }
186 
187 #endif
crashhandler.h
Logger::error
void error(const std::string &what)
Definition: logger.h:54
stdext::format
std::string format()
Definition: format.h:82
Application::getName
const std::string & getName()
Definition: application.h:51
g_logger
Logger g_logger
Definition: logger.cpp:35
BUILD_TYPE
#define BUILD_TYPE
Definition: const.h:40
BUILD_REVISION
#define BUILD_REVISION
Definition: const.h:36
g_app
ConsoleApplication g_app
Definition: consoleapplication.cpp:32
stdext::date_time_string
std::string date_time_string()
Get current date and time in a std::string.
Definition: string.cpp:48
BUILD_COMMIT
#define BUILD_COMMIT
Definition: const.h:32
global.h
Logger::info
void info(const std::string &what)
Definition: logger.h:52
application.h
Application::getVersion
const std::string & getVersion()
Definition: application.h:53