OpenShot Library | libopenshot  0.1.9
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  // Linux and Mac stack unwinding
202  // Storage array for stack trace address data
203  void* addrlist[max_frames+1];
204 
205  // Retrieve current stack addresses
206  unsigned int addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* ));
207 
208  if ( addrlen == 0 )
209  {
210  fprintf(out, " No stack trace found (addrlen == 0)\n");
211  ZmqLogger::Instance()->LogToFile(" No stack trace found (addrlen == 0)\n");
212  return;
213  }
214 
215  // Resolve addresses into strings containing "filename(function+address)",
216  // Actually it will be ## program address function + offset
217  // this array must be free()-ed
218  char** symbollist = backtrace_symbols( addrlist, addrlen );
219 
220  size_t funcnamesize = 1024;
221  char funcname[1024];
222 
223  // Iterate over the returned symbol lines. Skip the first 4, it is the
224  // address of this function.
225  for ( unsigned int i = 4; i < addrlen; i++ )
226  {
227  char* begin_name = NULL;
228  char* begin_offset = NULL;
229  char* end_offset = NULL;
230 
231  // Find parentheses and +address offset surrounding the mangled name
232 #ifdef DARWIN
233  // OSX style stack trace
234  for ( char *p = symbollist[i]; *p; ++p )
235  {
236  if (( *p == '_' ) && ( *(p-1) == ' ' ))
237  begin_name = p-1;
238  else if ( *p == '+' )
239  begin_offset = p-1;
240  }
241 
242  if ( begin_name && begin_offset && ( begin_name < begin_offset ))
243  {
244  *begin_name++ = '\0';
245  *begin_offset++ = '\0';
246 
247  // Mangled name is now in [begin_name, begin_offset) and caller
248  // offset in [begin_offset, end_offset). now apply
249  // __cxa_demangle():
250  int status;
251  char* ret = abi::__cxa_demangle( begin_name, &funcname[0], &funcnamesize, &status );
252  if ( status == 0 )
253  {
254  funcname = ret; // Use possibly realloc()-ed string
255  fprintf( out, " %-30s %-40s %s\n", symbollist[i], funcname, begin_offset );
256  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << funcname << " " << begin_offset << endl;
257  } else {
258  // Demangling failed. Output function name as a C function with
259  // no arguments.
260  fprintf( out, " %-30s %-38s() %s\n", symbollist[i], begin_name, begin_offset );
261  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(38) << begin_name << " " << begin_offset << endl;
262  }
263 
264 #else // !DARWIN - but is posix
265  // not OSX style
266  // ./module(function+0x15c) [0x8048a6d]
267  for ( char *p = symbollist[i]; *p; ++p )
268  {
269  if ( *p == '(' )
270  begin_name = p;
271  else if ( *p == '+' )
272  begin_offset = p;
273  else if ( *p == ')' && ( begin_offset || begin_name ))
274  end_offset = p;
275  }
276 
277  if ( begin_name && end_offset && ( begin_name < end_offset ))
278  {
279  *begin_name++ = '\0';
280  *end_offset++ = '\0';
281  if ( begin_offset )
282  *begin_offset++ = '\0';
283 
284  // Mangled name is now in [begin_name, begin_offset) and caller
285  // offset in [begin_offset, end_offset). now apply
286  // __cxa_demangle():
287  int status = 0;
288  char* ret = abi::__cxa_demangle( begin_name, funcname, &funcnamesize, &status );
289  char* fname = begin_name;
290  if ( status == 0 )
291  fname = ret;
292 
293  if ( begin_offset )
294  {
295  fprintf( out, " %-30s ( %-40s + %-6s) %s\n", symbollist[i], fname, begin_offset, end_offset );
296  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << begin_offset << " " << end_offset << endl;
297 
298  } else {
299  fprintf( out, " %-30s ( %-40s %-6s) %s\n", symbollist[i], fname, "", end_offset );
300  stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << end_offset << endl;
301 
302  }
303 #endif // !DARWIN - but is posix
304  } else {
305  // Couldn't parse the line? print the whole line.
306  fprintf(out, " %-40s\n", symbollist[i]);
307  stack_output << left << " " << setw(40) << symbollist[i] << endl;
308  }
309  }
310 
311  // Free array
312  free(symbollist);
313 #endif
314 
315  // Write stacktrace to file (if log path set)
316  ZmqLogger::Instance()->LogToFile(stack_output.str());
317 
318  fprintf(out, "---- End of Stack Trace ----\n");
319  ZmqLogger::Instance()->LogToFile("---- End of Stack Trace ----\n");
320 }
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.