UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
win_backtrace.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 ,
3  * Cloud Wu . All rights reserved.
4  *
5  * http://www.codingnow.com
6  *
7  * Use, modification and distribution are subject to the "New BSD License"
8  * as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.
9  */
10 
11 #include "win_local.h"
12 #include "../../common/http.h"
13 #ifdef HAVE_BFD_H
14 #include "../../shared/bfd.h"
15 
16 #include <excpt.h>
17 #include <imagehlp.h>
18 #include <psapi.h>
19 
20 #if defined _M_AMD64
21  #define ARCH_SW(x86, amd64) amd64
22 #elif defined _M_IX86
23  #define ARCH_SW(x86, amd64) x86
24 #endif
25 
26 static void _backtrace (struct output_buffer* ob, struct bfd_set* set, int depth, LPCONTEXT context)
27 {
28  char procname[MAX_PATH];
29  struct bfd_ctx* bc = nullptr;
30  HANDLE process = GetCurrentProcess();
31  HANDLE thread = GetCurrentThread();
32  STACKFRAME frame;
33  char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
34  char module_name_raw[MAX_PATH];
35 
36  GetModuleFileNameA(nullptr, procname, sizeof(procname));
37 
38  OBJZERO(frame);
39 
40  frame.AddrPC.Offset = context->ARCH_SW(Eip, Rip);
41  frame.AddrPC.Mode = AddrModeFlat;
42  frame.AddrStack.Offset = context->ARCH_SW(Esp, Rsp);
43  frame.AddrStack.Mode = AddrModeFlat;
44  frame.AddrFrame.Offset = context->ARCH_SW(Ebp, Rbp);
45  frame.AddrFrame.Mode = AddrModeFlat;
46 
47  while (StackWalk(ARCH_SW(IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64), process, thread, &frame, context,
48  0, SymFunctionTableAccess, SymGetModuleBase, 0)) {
49  const char* file = nullptr;
50  const char* func = nullptr;
51  unsigned line = 0;
52  DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
53  const char* module_name = "[unknown module]";
54  IMAGEHLP_SYMBOL* symbol = (IMAGEHLP_SYMBOL*) symbol_buffer;
55 
56  --depth;
57  if (depth < 0)
58  break;
59 
60  symbol->SizeOfStruct = sizeof(*symbol) + 255;
61  symbol->MaxNameLength = 254;
62 
63  if (module_base && GetModuleFileNameA((HINSTANCE) module_base, module_name_raw, MAX_PATH)) {
64  module_name = module_name_raw;
65  bc = get_bc(ob, set, module_name);
66  }
67 
68  if (bc) {
69  find(bc, frame.AddrPC.Offset, &file, &func, &line);
70  }
71 
72  if (file == nullptr) {
73  ARCH_SW(DWORD, DWORD64) dummy = 0;
74  if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) {
75  file = symbol->Name;
76  } else {
77  file = "[unknown file]";
78  }
79  }
80  if (func == nullptr) {
81  output_print(ob, "0x%x : %s : %s \n", frame.AddrPC.Offset,
82  module_name, file);
83  } else {
84  output_print(ob, "0x%x : %s : %s (%d) : in function (%s) \n",
85  frame.AddrPC.Offset, module_name, file, line, func);
86  }
87  }
88 }
89 
90 static LPTOP_LEVEL_EXCEPTION_FILTER g_prev = nullptr;
91 #define BUFFER_MAX (16*1024)
92 static char* g_output = nullptr;
93 
94 static LONG WINAPI exception_filter (LPEXCEPTION_POINTERS info)
95 {
96  struct output_buffer ob;
97  SYSTEMTIME timeInfo;
98  OSVERSIONINFOEX osInfo;
99  const char* dumpFile = "crashdump.txt";
100 
101  GetSystemTime(&timeInfo);
102 
103  OBJZERO(osInfo);
104  osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
105  if (!GetVersionEx((OSVERSIONINFO*)&osInfo)) {
106  osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
107  GetVersionEx((OSVERSIONINFO*)&osInfo);
108  }
109 
110  output_init(&ob, g_output, BUFFER_MAX);
111 
112  if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) {
113  output_print(&ob, "Failed to init symbol context\n");
114  } else {
115  struct bfd_set* set = (struct bfd_set *)calloc(1, sizeof(*set));
116  bfd_init();
117  _backtrace(&ob, set, 128, info->ContextRecord);
118  release_set(set);
119 
120  SymCleanup(GetCurrentProcess());
121  }
122 
123  FILE* crash = Sys_Fopen(dumpFile, "w");
124  if (crash != nullptr) {
125  fprintf(crash, "======start======\n");
126  fprintf(crash, "Date: %.4d-%.2d-%.2d\n",
127  timeInfo.wYear, timeInfo.wMonth, timeInfo.wDay);
128  fprintf(crash, "Windows version %lu.%lu (Build %lu) %s\n",
129  osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.szCSDVersion);
130  fprintf(crash, BUILDSTRING ", cpu: " CPUSTRING ", version: " UFO_VERSION "\n\n");
131  fprintf(crash, "%s", g_output);
132  fprintf(crash, "======end========\n");
133  fclose(crash);
134  }
135  fputs(g_output, stderr);
136 
137  const int ret = MessageBox(nullptr, "Would you like to upload this crash dump and your ufoconsole.log? This will help the developers to fix the problem.", GAME_TITLE_LONG" Fatal Error", MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2);
138  if (ret == IDYES)
139  Com_UploadCrashDump(dumpFile);
140 
141  return 0;
142 }
143 
144 static void backtrace_register (void)
145 {
146  if (g_output == nullptr) {
147  g_output = (char*)malloc(BUFFER_MAX);
148  g_prev = SetUnhandledExceptionFilter(exception_filter);
149  }
150 }
151 
152 static void backtrace_unregister (void)
153 {
154  if (g_output) {
155  free(g_output);
156  SetUnhandledExceptionFilter(g_prev);
157  g_prev = nullptr;
158  g_output = nullptr;
159  }
160 }
161 #endif
162 
163 void Sys_BacktraceInit (void)
164 {
165 #ifdef HAVE_BFD_H
166  backtrace_register();
167 #endif
168 }
169 
171 {
172 #ifdef HAVE_BFD_H
173  backtrace_unregister();
174 #endif
175 }
#define CPUSTRING
Definition: common.h:109
#define FILE
Definition: test_webapi.cpp:30
void Sys_BacktraceInit(void)
Win32-specific UFO header file.
#define UFO_VERSION
Definition: common.h:36
#define GAME_TITLE_LONG
Definition: common.h:38
#define OBJZERO(obj)
Definition: shared.h:178
#define BUILDSTRING
Definition: common.h:121
void Com_UploadCrashDump(const char *crashDumpFile)
Definition: common.cpp:683
void Sys_BacktraceShutdown(void)
FILE * Sys_Fopen(const char *filename, const char *mode)
Definition: unix_files.cpp:240