OpenShot Library | libopenshot  0.1.3
CrashHandler.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @brief Source file for CrashHandler class
4  * @author Jonathan Thomas <jonathan@openshot.org>
5  *
6  * @section LICENSE
7  *
8  * Copyright (c) 2008-2014 OpenShot Studios, LLC
9  * <http://www.openshotstudios.com/>. This file is part of
10  * OpenShot Library (libopenshot), an open-source project dedicated to
11  * delivering high quality video editing and animation solutions to the
12  * world. For more information visit <http://www.openshot.org/>.
13  *
14  * OpenShot Library (libopenshot) is free software: you can redistribute it
15  * and/or modify it under the terms of the GNU Lesser General Public License
16  * as published by the Free Software Foundation, either version 3 of the
17  * License, or (at your option) any later version.
18  *
19  * OpenShot Library (libopenshot) is distributed in the hope that it will be
20  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 #include "../include/CrashHandler.h"
29 
30 using namespace std;
31 using namespace openshot;
32 
33 
34 // Global reference to logger
35 CrashHandler *CrashHandler::m_pInstance = NULL;
36 
37 // Create or Get an instance of the logger singleton
38 CrashHandler *CrashHandler::Instance()
39 {
40  if (!m_pInstance) {
41  // Create the actual instance of crash handler only once
42  m_pInstance = new CrashHandler;
43 
44 #ifdef __MINGW32__
45  // TODO: Windows exception handling methods
46  signal(SIGSEGV, CrashHandler::abortHandler);
47 
48 #else
49  struct sigaction sa;
50  sa.sa_flags = SA_SIGINFO;
51  sa.sa_sigaction = CrashHandler::abortHandler;
52  sigemptyset( &sa.sa_mask );
53 
54  // Register abortHandler function callback
55  sigaction( SIGABRT, &sa, NULL );
56  sigaction( SIGSEGV, &sa, NULL );
57  sigaction( SIGBUS, &sa, NULL );
58  sigaction( SIGILL, &sa, NULL );
59  sigaction( SIGFPE, &sa, NULL );
60  sigaction( SIGPIPE, &sa, NULL );
61 #endif
62  }
63 
64  return m_pInstance;
65 }
66 
67 #ifdef __MINGW32__
68 // Windows exception handler
69 void CrashHandler::abortHandler(int signum)
70 {
71  // Associate each signal with a signal name string.
72  const char* name = NULL;
73  switch( signum )
74  {
75  case SIGABRT: name = "SIGABRT"; break;
76  case SIGSEGV: name = "SIGSEGV"; break;
77  case SIGILL: name = "SIGILL"; break;
78  case SIGFPE: name = "SIGFPE"; break;
79  }
80 
81  // Notify the user which signal was caught
82  if ( name )
83  fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
84  else
85  fprintf( stderr, "Caught signal %d\n", signum );
86 
87  // Dump a stack trace.
88  printStackTrace(stderr, 63);
89 
90  // Quit
91  exit( signum );
92 }
93 #else
94 // Linux and Mac Exception Handler
95 void CrashHandler::abortHandler( int signum, siginfo_t* si, void* unused )
96 {
97  // Associate each signal with a signal name string.
98  const char* name = NULL;
99  switch( signum )
100  {
101  case SIGABRT: name = "SIGABRT"; break;
102  case SIGSEGV: name = "SIGSEGV"; break;
103  case SIGBUS: name = "SIGBUS"; break;
104  case SIGILL: name = "SIGILL"; break;
105  case SIGFPE: name = "SIGFPE"; break;
106  case SIGPIPE: name = "SIGPIPE"; break;
107  }
108 
109  // Notify the user which signal was caught
110  if ( name )
111  fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
112  else
113  fprintf( stderr, "Caught signal %d\n", signum );
114 
115  // Dump a stack trace.
116  printStackTrace(stderr, 63);
117 
118  // Quit
119  exit( signum );
120 }
121 #endif
122 
123 void CrashHandler::printStackTrace(FILE *out, unsigned int max_frames)
124 {
125  fprintf(out, "---- Unhandled Exception: Stack Trace ----\n");
126  ZmqLogger::Instance()->LogToFile("---- Unhandled Exception: Stack Trace ----\n");
127  stringstream stack_output;
128 
129 #ifdef __MINGW32__
130  // Windows stack unwinding
131  HANDLE process = GetCurrentProcess();
132  HANDLE thread = GetCurrentThread();
133 
134  CONTEXT context;
135  memset(&context, 0, sizeof(CONTEXT));
136  context.ContextFlags = CONTEXT_FULL;
137  RtlCaptureContext(&context);
138 
139  SymInitialize(process, NULL, TRUE);
140 
141  DWORD image;
142  STACKFRAME64 stackframe;
143  ZeroMemory(&stackframe, sizeof(STACKFRAME64));
144 
145 #ifdef _M_IX86
146  image = IMAGE_FILE_MACHINE_I386;
147  stackframe.AddrPC.Offset = context.Eip;
148  stackframe.AddrPC.Mode = AddrModeFlat;
149  stackframe.AddrFrame.Offset = context.Ebp;
150  stackframe.AddrFrame.Mode = AddrModeFlat;
151  stackframe.AddrStack.Offset = context.Esp;
152  stackframe.AddrStack.Mode = AddrModeFlat;
153 #elif _M_X64
154  image = IMAGE_FILE_MACHINE_AMD64;
155  stackframe.AddrPC.Offset = context.Rip;
156  stackframe.AddrPC.Mode = AddrModeFlat;
157  stackframe.AddrFrame.Offset = context.Rsp;
158  stackframe.AddrFrame.Mode = AddrModeFlat;
159  stackframe.AddrStack.Offset = context.Rsp;
160  stackframe.AddrStack.Mode = AddrModeFlat;
161 #elif _M_IA64
162  image = IMAGE_FILE_MACHINE_IA64;
163  stackframe.AddrPC.Offset = context.StIIP;
164  stackframe.AddrPC.Mode = AddrModeFlat;
165  stackframe.AddrFrame.Offset = context.IntSp;
166  stackframe.AddrFrame.Mode = AddrModeFlat;
167  stackframe.AddrBStore.Offset = context.RsBSP;
168  stackframe.AddrBStore.Mode = AddrModeFlat;
169  stackframe.AddrStack.Offset = context.IntSp;
170  stackframe.AddrStack.Mode = AddrModeFlat;
171 #endif
172 
173  // Loop through the entire stack
174  for (size_t i = 0; i < max_frames; i++) {
175 
176  BOOL result = StackWalk64(
177  image, process, thread,
178  &stackframe, &context, NULL,
179  SymFunctionTableAccess64, SymGetModuleBase64, NULL);
180 
181  if (i <= 2) { continue; } // Skip the first 3 elements (those relate to these functions)
182  if (!result) { break; }
183 
184  char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
185  PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
186  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
187  symbol->MaxNameLen = MAX_SYM_NAME;
188  WINBOOL found_symbol = SymFromAddr(process, stackframe.AddrPC.Offset, NULL, symbol);
189 
190  if (found_symbol) {
191  printf("[%i] %s, address 0x%0X\n", i, symbol->Name, symbol->Address);
192  stack_output << left << setw(30) << symbol->Name << " " << setw(40) << std::hex << symbol->Address << std::dec << endl;
193  } else {
194  printf("[%i] ???\n", i);
195  stack_output << left << setw(30) << "???" << endl;
196  }
197  }
198  SymCleanup(process);
199 
200 #else
201  // Storage array for stack trace address data
202  void* addrlist[max_frames+1];
203 
204  // Retrieve current stack addresses
205  unsigned int addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* ));
206 
207  if ( addrlen == 0 )
208  {
209  fprintf(out, " No stack trace found (addrlen == 0)\n");
210  ZmqLogger::Instance()->LogToFile(" No stack trace found (addrlen == 0)\n");
211  return;
212  }
213 
214  // Resolve addresses into strings containing "filename(function+address)",
215  // Actually it will be ## program address function + offset
216  // this array must be free()-ed
217  char** symbollist = backtrace_symbols( addrlist, addrlen );
218 
219  size_t funcnamesize = 1024;
220  char funcname[1024];
221 
222  // Iterate over the returned symbol lines. Skip the first 4, it is the
223  // address of this function.
224  for ( unsigned int i = 4; i < addrlen; i++ )
225  {
226  char* begin_name = NULL;
227  char* begin_offset = NULL;
228  char* end_offset = NULL;
229 
230  // Find parentheses and +address offset surrounding the mangled name
231 #ifdef DARWIN
232  // OSX style stack trace
233  for ( char *p = symbollist[i]; *p; ++p )
234  {
235  if (( *p == '_' ) && ( *(p-1) == ' ' ))
236  begin_name = p-1;
237  else if ( *p == '+' )
238  begin_offset = p-1;
239  }
240 
241  if ( begin_name && begin_offset && ( begin_name < begin_offset ))
242  {
243  *begin_name++ = '\0';
244  *begin_offset++ = '\0';
245 
246  // Mangled name is now in [begin_name, begin_offset) and caller
247  // offset in [begin_offset, end_offset). now apply
248  // __cxa_demangle():
249  int status;
250  char* ret = abi::__cxa_demangle( begin_name, &funcname[0], &funcnamesize, &status );
251  if ( status == 0 )
252  {
253  funcname = ret; // Use possibly realloc()-ed string
254  fprintf( out, " %-30s %-40s %s\n", symbollist[i], funcname, begin_offset );
255  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << funcname << " " << begin_offset << endl;
256  } else {
257  // Demangling failed. Output function name as a C function with
258  // no arguments.
259  fprintf( out, " %-30s %-38s() %s\n", symbollist[i], begin_name, begin_offset );
260  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(38) << begin_name << " " << begin_offset << endl;
261  }
262 
263 #else // !DARWIN - but is posix
264  // not OSX style
265  // ./module(function+0x15c) [0x8048a6d]
266  for ( char *p = symbollist[i]; *p; ++p )
267  {
268  if ( *p == '(' )
269  begin_name = p;
270  else if ( *p == '+' )
271  begin_offset = p;
272  else if ( *p == ')' && ( begin_offset || begin_name ))
273  end_offset = p;
274  }
275 
276  if ( begin_name && end_offset && ( begin_name < end_offset ))
277  {
278  *begin_name++ = '\0';
279  *end_offset++ = '\0';
280  if ( begin_offset )
281  *begin_offset++ = '\0';
282 
283  // Mangled name is now in [begin_name, begin_offset) and caller
284  // offset in [begin_offset, end_offset). now apply
285  // __cxa_demangle():
286  int status = 0;
287  char* ret = abi::__cxa_demangle( begin_name, funcname, &funcnamesize, &status );
288  char* fname = begin_name;
289  if ( status == 0 )
290  fname = ret;
291 
292  if ( begin_offset )
293  {
294  fprintf( out, " %-30s ( %-40s + %-6s) %s\n", symbollist[i], fname, begin_offset, end_offset );
295  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << begin_offset << " " << end_offset << endl;
296 
297  } else {
298  fprintf( out, " %-30s ( %-40s %-6s) %s\n", symbollist[i], fname, "", end_offset );
299  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << end_offset << endl;
300 
301  }
302 #endif // !DARWIN - but is posix
303  } else {
304  // Couldn't parse the line? print the whole line.
305  fprintf(out, " %-40s\n", symbollist[i]);
306  stack_output << left << " " << setw(40) << symbollist[i] << endl;
307  }
308  }
309 
310  // Free array
311  free(symbollist);
312 #endif
313 
314  // Write stacktrace to file (if log path set)
315  ZmqLogger::Instance()->LogToFile(stack_output.str());
316 
317  fprintf(out, "---- End of Stack Trace ----\n");
318  ZmqLogger::Instance()->LogToFile("---- End of Stack Trace ----\n");
319 }
This class is designed to catch exceptions thrown by libc (SIGABRT, SIGSEGV, SIGILL, SIGFPE)
Definition: CrashHandler.h:53
This namespace is the default namespace for all code in the openshot library.